Previous Page
Next Page

Using ref and out Parameters

When you pass an argument to a method, the corresponding parameter is initialized with a copy of the argument. This is true regardless of whether the parameter is a value type (such as an int), or a reference type (such as a WrappedInt). This arrangement means it's impossible for any change to the parameter to affect the value of the argument passed in. For example, in the following code, the value output to the console is 42 and not 43. The DoWork method increments a copy of the argument (arg), and not the original argument:

static void DoWork(int param) 
{ 
    param++; 
} 
 
static void Main() 
{ 
    int arg = 42; 
    DoWork(arg); 
    Console.WriteLine(arg); // writes 42 not 43 
}

In the previous exercise you saw that if the parameter to a method is a reference type, then any changes made by using that parameter change the data referenced by the argument passed in. The key point is that, although the data that was referenced changed, the parameter itself did not—it still referenced the same object. In other words, although it is possible to modify the object that the argument refers to through the parameter, it's not possible to modify the argument itself (for example, to set it to refer to a completely new object). Most of the time, this guarantee is very useful and can help to reduce the number of bugs in a program. Occasionally, however, you might want to write a method that actually needs to modify an argument. C# provides the ref and out keywords to allow you to do this.

Creating ref Parameters

If you prefix a parameter with the ref keyword, the parameter becomes an alias for (or a reference to) the actual argument rather than a copy of the argument. When using a ref parameter, anything you do to the parameter, you also do to the original argument, because the parameter and the argument both reference the same object. When you pass an argument to a ref parameter, you must also prefix the argument with the ref keyword. This syntax provides a useful visual indication that the argument might change. Here's the previous example again, this time modified to use the ref keyword:

static void DoWork(ref int param) // using ref 
{ 
    param++; 
}
static void Main() 
{ 
    int arg = 42; 
    DoWork(ref arg);         // using ref 
    Console.WriteLine(arg); // writes 43 
}

This time, because the DoWork method is passed a reference to the original argument rather than a copy, any changes it makes by using this reference also change the original argument, so the value 43 is displayed on the console.

The rule that you must assign a value to a variable before you can use it still applies to ref arguments. For example, in the following example, arg is not initialized, so this code will not compile. This failure is because param++ inside DoWork is really arg++, and arg++ is allowed only if arg has a defined value:

static void DoWork(ref int param)  
{ 
    param++; 
}
static void Main() 
{ 
    int arg;                // not initialized 
    DoWork(ref arg);          
    Console.WriteLine(arg); 
}
Creating out Parameters

The compiler checks that a ref parameter has been assigned a value before calling the method. However, there may be times when you want the method itself to initialize the parameter, and so pass an uninitialized argument to the method. The out keyword allows you to do this.

The out keyword is very similar to the ref keyword. You can prefix a parameter with the out keyword so that the parameter becomes an alias for the argument. As when using ref, anything you do to the parameter, you also do to the original argument. When you pass an argument to an out parameter, you must also prefix the argument with the out keyword.

The keyword out is short for output. When you pass an out parameter to a method, the method has to assign a value to it. The following example does not compile because DoWork does not assign a value to param:

static void DoWork(out int param) 
{ 
    // Do nothing 
}

However, the following example does compile because DoWork assigns a value to param:

static void DoWork(out int param) 
{ 
    param = 42; 
}

Because an out parameter must be assigned a value by the method, you're allowed to call the method without initializing its argument. When the method call has finished, the argument must have been assigned a value. For example, the following code calls DoWork to initialize the variable arg, which is then displayed on the console:

static void DoWork(out int param) 
{ 
    param = 42; 
}
 
static void Main() 
{ 
    int arg;                // not initialized 
    DoWork(out arg); 
    Console.WriteLine(arg); // writes 42 
}
Use ref parameters
  1. Return to the Parameters project in Visual Studio 2005.

  2. Display the Pass.cs source file in the Code and Text Editor window.

  3. Edit the Value method to accept its int parameter as a ref parameter.

    The Value method should look like this:

    class Pass 
    { 
        public static void Value(ref int param) 
        { 
            param = 42; 
        } 
        ... 
    }
  4. Display the Program.cs source file in the Code and Text Editor window.

  5. Edit the third statement of the Entrance method so that the Pass.Value method call passes its argument as a ref parameter.

    The Entrance method should now look like this:

    class Application 
    { 
        static void Entrance() 
        { 
            int i = 0; 
            Console.WriteLine(i); 
            Pass.Value(ref i); 
            Console.WriteLine(i); 
            ... 
        } 
    }
  6. On the Debug menu, click Start Without Debugging to build and run the program.

    This time, the first two values written to the console window are 0 and 42. This result shows that the call to the Pass.Value method has modified the argument i.

  7. Press the Enter key to close the application.

NOTE
You can use the ref and out modifiers on reference type parameters as well as value type parameters. The effect is exactly the same. The parameter becomes an alias for the argument. If you reassigned the parameter to a newly constructed object, you would actually be reassigning the argument to the newly constructed object.

Previous Page
Next Page