Humblecoder

Caution, this blog may be ironically named

Passing Reference Types Using Ref, Take Two

| Comments

In my last post I talked about passing reference types using the ref keyword but it didn’t make a lot of sense.  So I just want to go over it again, hopefully making a bit more sense.

When a method is called in C# a copy of the all parameters are given to the method.  This is fairly obvious with value types because if we change the value of an int, for example, the caller does not get the updated value.

However this is not so clear for reference types.  The called method can update the state of the object it was passed, for example append extra data to a StringBuilder, and the caller’s object will have these updates.  This can lead to confusion about what is really happening, it looks as if the StringBuilder was passed by reference but a copy of the reference to it was taken.

It maybe subtle semantics under normal use but it becomes key to understanding behaviour when the ref keyword is used.  For value types this means that if we increment an int we are passed, the caller will have the new value too. For reference types the reference we are passed is actually a reference to the caller’s reference. Meaning if we assign a new reference to it, the caller will get the new reference.

We can demonstrate this with the following code:

static void Main(string[] args)
{
    StringBuilder sb = new StringBuilder();
    sb.AppendLine("Added by Main");  
    AddToSBPassedAsNormal(sb);
    Console.Write(sb.ToString());
    //Output: Added by Main
    //        AddToSBPassedAsNormal  
    AddToSBPassedByRef(ref sb);
    Console.Write(sb.ToString());
    //Output: AddToSBPassedByRef  
    AddToSBPassedAsNormalNewUsed(sb);
    Console.Write(sb.ToString());
    //Output: AddToSBPassedByRef  
    Console.ReadLine();
}  
private static void AddToSBPassedAsNormal(StringBuilder sb)
{
    sb.AppendLine("AddToSBPassedAsNormal");
}  
private static void AddToSBPassedByRef(ref StringBuilder sb)
{
    sb = new StringBuilder();
    sb.AppendLine("AddToSBPassedByRef");
}  
private static void AddToSBPassedAsNormalNewUsed(StringBuilder sb)
{
    sb = new StringBuilder();
    sb.AppendLine("AddToSBPassedAsNormalNewUsed");
}

From the code above we can see that the StringBuilder after the first method contains both strings.  But after the method call, where it is passed by ref, the previously entered data has been lost and, finally, using new when not being passing by reference has no effect on Main()’s reference to the StringBuilder.

Before passing a reference type using the ref keyword you must think carefully about the implications of the caller changing the reference.  As it can lead to some esoteric and difficult to track down bugs.

Comments