To ref or not to ref

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

Scenario 1:

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?

Scenario 2:

   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.

Scenario 3:

   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.

Scenario 5:

   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!

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

PS: Sorry for the confusion earlier folks. I hope I've fixed it this time.

9 Comments

  • that really helps! now I understand why we have to use ref.

  • First I want to thank you for writing these types of articles, as I remember a decade ago how long it took me (as a .NET beginner) to get my head around these issues.

    Your article was correct right until the last part, where you remove the ref from the string example. It would be really good if you could update your post as this is really confusing for new programmers who might be reading your blog.

    As a side note, you probably should not have raised immutability as that is a separate concept that could confuse readers and has nothing to do with what you are observing with the string example. It is best to avoid using string as an example because of the kind of optimisations the compiler does with strings (knowing they are immutable). But again all this has nothing do to with the mistake in your article.

    The key mistake in your article is where you say "The output should still be the same as the above right, since string is a reference type?". This is incorrect, of course the output should not be the same as above and this has nothing to do with immutability. You are assigning name inside the CallSomeMethod to a new reference type and this should have no effect at all on the name variable in your main program. At the point of the assignment name="def" you now have two different variables pointing to two different objects allocated on the heap.

    Remember that reference types are stored on the heap and managed by the garbage collector. If you pass any reference type (immutable or not) without the ref keyword to a separate method, and inside that method you assign the parameter to a new instance, you will *never* modify the original object. Please try this with any reference type (immutable or not).

    You need a conceptual model of what is happening. The way I think about it is this:
    If I have a variable varA of reference type, this is really just a pointer to something managed by the garbage collector. If I pass it to a method without the ref keyword it 'copies' the pointer value to varB. At this point varA and varB are pointing to the same object managed by the garbage collector. If inside the method I say varB = new MyObject(); then we now have varA pointing to the original object and varB pointing to the new object. Thus when the method exits varA absolutely SHOULD be pointing to the object it was before the method call.

    I admit these are complex issues, and there are whole chapters of books written on these topics if you are interested in following up. Also please feel free to email me at davidandrewtaylor at hotmail.com if you want to discuss further.

    Again, in general it was a good article, please just fix the last 10% to be accurate and maybe remove the talk of immutability as that might confuse your readers.
    Kind Regards, David

  • (Numbering examples using console output screens, start index 1).

    If your final example (6) was meant to resemble example (1), it doesn't. In example 1, you were manipulating properties of the passed in object (indeed, modifying the instance). Whereas example (6) resembles example (3) - you're just assigning a new value to the name variable within the CallSomeMethod method.

    This has nothing to do with immutability, as such.

  • This is a little bit confusing.

    Ref refers to the _variable_ being passed by reference, nothing to do with what its contents are. This means you can access the variable (not just its contents) within the method via the parameter.

    The "one more change to the CallSomeMethod method." you mention is actually the key here - you need to emphasise that you're now creating a whole new instance of the object.

    In order to get that new instance back out of the method you need to return it. You can do that indirectly by altering the variable passed by reference as a parameter.

    However you could do with pointing out that this is demo code and in all but a few specific scenarios (e.g. TryParse), it's considered bad code-style.

    Fundamentally, you're not "keeping the objects in sync", by passing them using the ref keyword, there are still two independent objects. You're just changing which one the variable is refering to by passing the variable's reference rather than its value so that it can be altered by the method.

    However something noteworthy happens when crossing AppDomains: as an simple optimisation, paramters are not serialized back to the calling AppDomain, so updates to parameters passed by value are not seen at the client. Marking the parameter as ref forces the value to be serailized back so that the new object can repace the value referenced by the variable.

  • Hi All,

    Thanks a bunch for you comments. There was an error in my style of writing, not my way of understanding the concepts. Either way, it was my bad. I have modified my statement(s) in the blog. Hope this helps.

    Arun

  • I'd just like to comment that if you're doing web/desktop development, modifying object pointers by using ref parameters is not a good practice at all. If you're going to create a new instance, you should return it from CallSomeMethod() instead of using ref. That style is more explicit and won't trip up whoever has to maintain your code.

  • @@Ryan, you do have a point. But my intention was to show the difference of having a ref or no ref parameter.

    Arun

  • I don't really feel that the article clarifies the topic.

    Using the string type for examples like this is difficult because string has special behaviors and one usually wants to illustrate standard behaviors.

    The point of the ref parameter is to allow a method to replace original object instead of only modifying its properties.

  • In general, if you are not using the value passed in (as you aren't in these examples), you should use the "out" keyword instead of "ref". "out" and "ref" generate the same MSIL code, but enable different compiler checks.

    If neither out nor ref is used, the compiler will require that the variable be assigned a value BEFOERE the method call.

    If out is used, the compiler will require that the variable be assigned a value WITHIN the method call.

    If ref is used, the compiler does not preform any assignment check.

Comments have been disabled for this content.