To ref or not to ref

Posted by nmarun on ASP.net Weblogs See other posts from ASP.net Weblogs or by nmarun
Published on Wed, 29 Dec 2010 14:44:00 GMT Indexed on 2010/12/29 14:54 UTC
Read the original article Hit count: 594

Filed under:
|

So the question is what is the point of passing a reference type along with the ref keyword?

I have an Employee class as below:

   1: public class Employee
   2: {
   3:     public string FirstName { get; set; }
   4:     public string LastName { get; set; }
   5:  
   6:     public override string ToString()
   7:     {
   8:         return string.Format("{0}-{1}", FirstName, LastName);
   9:     }
  10: }

In my calling class, I say:

   1: class Program
   2: {
   3:     static void Main()
   4:     {
   5:         Employee employee = new Employee
   6:                                 {
   7:                                     FirstName = "John",
   8:                                     LastName = "Doe"
   9:                                 };
  10:         Console.WriteLine(employee);
  11:         CallSomeMethod(employee);
  12:         Console.WriteLine(employee);
  13:     }
  14:  
  15:     private static void CallSomeMethod(Employee employee)
  16:     {
  17:         employee.FirstName = "Smith";
  18:         employee.LastName = "Doe";
  19:     }
  20: }

 

After having a look at the code, you’ll probably say,

Well, an instance of a class gets passed as a reference, so any changes to the instance inside the CallSomeMethod, actually modifies the original object. Hence the output will be ‘John-Doe’ on the first call and ‘Smith-Doe’ on the second.

And you’re right:

image

So the question is what’s the use of passing this Employee parameter as a ref?

   1: class Program
   2: {
   3:     static void Main()
   4:     {
   5:         Employee employee = new Employee
   6:                                 {
   7:                                     FirstName = "John",
   8:                                     LastName = "Doe"
   9:                                 };
  10:         Console.WriteLine(employee);
  11:         CallSomeMethod(ref employee);
  12:         Console.WriteLine(employee);
  13:     }
  14:  
  15:     private static void CallSomeMethod(ref Employee employee)
  16:     {
  17:         employee.FirstName = "Smith";
  18:         employee.LastName = "Doe";
  19:     }
  20: }

The output is still the same:

image

Ok, so is there really a need to pass a reference type using the ref keyword? I’ll remove the ‘ref’ keyword and make one more change to the CallSomeMethod method.

   1: class Program
   2: {
   3:     static void Main()
   4:     {
   5:         Employee employee = new Employee
   6:                                 {
   7:                                     FirstName = "John",
   8:                                     LastName = "Doe"
   9:                                 };
  10:         Console.WriteLine(employee);
  11:         CallSomeMethod(employee);
  12:         Console.WriteLine(employee);
  13:     }
  14:  
  15:     private static void CallSomeMethod(Employee employee)
  16:     {
  17:         employee = new Employee
  18:                         {
  19:                             FirstName = "Smith", 
  20:                             LastName = "John"
  21:                         };
  22:     }
  23: }

In line 17 you’ll see I’ve ‘new’d up the incoming Employee parameter and then set its properties to new values. The output tells me that the original instance of the Employee class does not change.

image

Huh? But an instance of a class gets passed by reference, so why did the values not change on the original instance or how do I keep the two instances in-sync all the times?

Aah, now here’s the answer. In order to keep the objects in sync, you pass them using the ‘ref’ keyword.

   1: class Program
   2: {
   3:     static void Main()
   4:     {
   5:         Employee employee = new Employee
   6:                                 {
   7:                                     FirstName = "John",
   8:                                     LastName = "Doe"
   9:                                 };
  10:         Console.WriteLine(employee);
  11:         CallSomeMethod(ref employee);
  12:         Console.WriteLine(employee);
  13:     }
  14:  
  15:     private static void CallSomeMethod(ref Employee employee)
  16:     {
  17:         employee = new Employee
  18:                         {
  19:                             FirstName = "Smith", 
  20:                             LastName = "John"
  21:                         };
  22:     }
  23: }

image

Viola! Now, to prove it beyond doubt, I said, let me try with another reference type: string.

   1: class Program
   2: {
   3:     static void Main()
   4:     {
   5:         string name = "abc";
   6:         Console.WriteLine(name);
   7:         CallSomeMethod(ref name);
   8:         Console.WriteLine(name);
   9:     }
  10:  
  11:     private static void CallSomeMethod(ref string name)
  12:     {
  13:         name = "def";
  14:     }
  15: }

The output was as expected, first ‘abc’ and then ‘def’ - proves the 'ref' keyword works here as well.

image

Now, what if I remove the ‘ref’ keyword? The output should still be the same as the above right, since string is a reference type?

   1: class Program
   2: {
   3:     static void Main()
   4:     {
   5:         string name = "abc";
   6:         Console.WriteLine(name);
   7:         CallSomeMethod(name);
   8:         Console.WriteLine(name);
   9:     }
  10:  
  11:     private static void CallSomeMethod(string name)
  12:     {
  13:         name = "def";
  14:     }
  15: }

Wrong, the output shows ‘abc’ printed twice.

image

Wait a minute… now how could this be? This is because string is an immutable type. This means that any time you modify an instance of string, new memory address is allocated to the instance. The effect is similar to ‘new’ing up the Employee instance inside the CallSomeMethod in the absence of the ‘ref’ keyword.

Verdict: ref key came to the rescue and saved the planet… again!

© ASP.net Weblogs or respective owner

Related posts about c#

Related posts about .NET