All About Ref and Out Type Parameters

This is among one of the most important questions asked in C# interviews. It is very well known that both ref and out parameters are used to pass an argument within a method.

But the question here is, what are the scenarios we need them and which one should we prefer.

We always heard that in C# there are two types of “types”:

  • Value types contain the data itself.

  • Reference types have their value as a reference to the data, instead of the data itself.

We are pretty much aware of the value type. So the main concern here is for reference type. In case of the reference type we can use either out or ref keyword.

Reference parameters actually never pass the value of a variable in the invoked method, instead they use the variable themselves.

From the point of storage, instead of allocating a new storage for that variable in the method declaration, the same storage space is used, so in this case the value of the variable in the member method and the value of the reference parameter will always remain the same.

Let's take now the output parameter. At the first glance they seems to be much like each other.

Output parameters specified at the time of calling doesn't need to have been assigned a value before it is passed to the invoked method. When the method is invoked completely we can read that variable as it is assigned by now.

Following the same pattern as reference parameters, output parameters also do not create a new storage location, but use the storage location of the variable specified on the invocation. Output parameters need the out modifier as part of both the declaration and the invocation. That means it's always clear when you're passing something as an output parameter.

Note: Reference parameters require ref modifier and Output parameters need the out modifier as part of both the invoking and invoked method.

  1. int x;  
  2. Foo(out x); // OK  
  3.    
  4. int x;  
  5. Foo(ref x); // ERROR  

Which one to choose

ref parameters are for data that might be modified, out parameters are for data that's an additional output for the function (for example int.TryParse) that are already using the return value for something.

We should use out unless we need ref.

It makes a big difference when the data needs to be marshalled, for example to another process that can be costly. So you want to avoid marshalling the initial value when the method doesn't make use of it.

Beyond that, it also shows the reader of the declaration or the call whether the initial value is relevant (and potentially preserved), or thrown away.

As a minor difference, an out parameter need not be initialized.

The following is an example of out: 

  1. string a, b;   
  2. person.GetBothNames(out a, out b);  

Where GetBothNames is a method to retrieve two values atomically, the method won't change the behaviour of whatever a and b are. If the call goes to a server in Hawaii, copying the initial values from here to Hawaii is a waste of bandwidth.

The following is a similar snippet using ref: 
  1. string a = String.Empty, b = String.Empty;  
  2.   
  3. person.GetBothNames(ref a, ref b);  

That however could confuse readers, because it looks like the initial values of a and b are relevant (though the method name would indicate they are not).

The following is an example for ref: 

  1. string name = textbox.Text;  
  2.   
  3. bool didModify = validator.SuggestValidName(ref name);  

Here the initial value is relevant to the method.

From the compiler's point of view

ref tells the compiler that the object is initialized before entering the function, whereas out tells the compiler that the object will be initialized inside the function.

So whereas ref is two-ways, out is out-only.

Both ref and out cannot be used in method overloading simultaneously. However, ref and out are treated differently at run-time but they are treated the same at compile time (the CLR doesn't differentiate between the two when it creates IL for ref and out).

Hence methods cannot be overloaded when one method takes a ref parameter and the other method takes an out parameter. The following two methods are identical in terms of compilation. 
  1. class Example1 {  
  2.     // Compiler error: "Cannot define overloaded  
  3.     // methods that differ only on ref and out".  
  4.     public void SampleMethod(out int i) {}  
  5.     public void SampleMethod(ref int i) {}  
  6. }  

However, method overloading can be done, if one method takes a ref or out argument and the other method takes a simple argument. The following example is perfectly valid to be overloaded. 

  1. class Example2  
  2. {  
  3.    public void SampleMethod(int i) { }     
  4.    public void SampleMethod(ref int i) { }  
  5. }  

Or:

  1. class Example3  
  2. {  
  3.    public void SampleMethod(int i) { }  
  4.    public void SampleMethod(out int i) { }  
  5. }  

Like all out parameters, an out parameter of an array type must be assigned before it is used; that is, it must be assigned by the callee.

  1. static void TestMethod1(out int[] arr)  
  2. {  
  3.    arr = new int[10]; // definite assignment of arr  
  4. }  
Like all ref parameters, a ref parameter of an array type must be definitely assigned by the caller. Therefore, there is no need to be definitely assigned by the callee. A ref parameter of an array type may be altered as a result of the call.
 
For example, the array can be assigned the null value or can be initialized to a different array. 
  1. static void TestMethod2(ref int[] arr)  
  2. {  
  3.    arr = new int[10]; // arr initialized to a different array  
  4. }  
Example: The array theArray is declared in the caller (the Main method), and initialized in the FillArray method. Then, the array elements are returned to the caller and displayed.
  1. class TestOut {  
  2.     static void FillArray(out int[] arr) {  
  3.         // Initialize the array:  
  4.         arr = new int[5] {  
  5.             1, 2, 3, 4, 5  
  6.         };  
  7.     }  
  8.   
  9.     static void Main() {  
  10.         int[] theArray; // Initialization is not required  
  11.   
  12.         // Pass the array to the callee using out:  
  13.         FillArray(out theArray);  
  14.   
  15.         // Display the array elements:  
  16.         System.Console.WriteLine("Array elements are:");  
  17.         for (int i = 0; i < theArray.Length; i++) {  
  18.             System.Console.Write(theArray[i] + " ");  
  19.         }  
  20.   
  21.         // Keep the console window open in debug mode.  
  22.         System.Console.WriteLine("Press any key to exit.");  
  23.         System.Console.ReadKey();  
  24.     }  
  25. }  

Output

The array elements are:
1 2 3 4 5
 
In this example, the array theArray is initialized in the caller (the Main method), and passed to the FillArray method using the ref parameter. Some of the array elements are updated in the FillArray method. Then, the array elements are returned to the caller and displayed.
  1. class TestRef {  
  2.     static void FillArray(ref int[] arr) {  
  3.         // Create the array on demand:  
  4.         if (arr == null) {  
  5.             arr = new int[10];  
  6.         }  
  7.         // Fill the array:  
  8.         arr[0] = 1111;  
  9.         arr[4] = 5555;  
  10.     }  
  11.   
  12.     static void Main() {  
  13.         // Initialize the array:  
  14.         int[] theArray = {  
  15.             1, 2, 3, 4, 5  
  16.         };  
  17.   
  18.         // Pass the array using ref:  
  19.         FillArray(ref theArray);  
  20.   
  21.         // Display the updated array:  
  22.         System.Console.WriteLine("Array elements are:");  
  23.         for (int i = 0; i < theArray.Length; i++) {  
  24.             System.Console.Write(theArray[i] + " ");  
  25.         }  
  26.   
  27.         // Keep the console window open in debug mode.  
  28.         System.Console.WriteLine("Press any key to exit.");  
  29.         System.Console.ReadKey();  
  30.     }  
  31. }  
Output
 
Array elements are:
1111 2 3 4 5555

The following are some points to remember:

  • Properties cannot be passed via out or ref, since properties are actually methods.

  • There is no "boxing" when a value type is passed by reference.

  • ref / out are not considered to be a part of the method signature at compile time, so methods cannot be overloaded, if the only difference between them is that one of the methods takes a ref argument and the other takes an out argument.
Thanks for visiting my blog.

Thanks for reading my article.
 
This is my first article here. Please provide your comments if you need any clarifications. It will be educational for me as well.


Similar Articles