Humblecoder

Caution, this blog may be ironically named

C# Basics: Ref’ing References, Ref’ing Hell

| Comments

For the past couple of weeks I’ve been deep in some legacy code.  The code has all kind of hidden charms, while I’m not going to be overly critical because it was written at a time when a .NET 2.0 application was cutting edge.  I uncovered this gem:

public void SomeHighLevelFunction(out String feedback)
{
    StringBuilder mySb = new StringBuilder();  
    _WorkerItem1.DoWorkOne(ref mySb);
    _WorkerItem2.DoWorkTwo(ref mySb);
    _WorkerItem3.DoWorkThree(ref mySb);  
    feedback = mySb.ToString();
}

Ignoring the void with the out String, why pass the StringBuilder by ref? This code was written by a migrating C++ programmer; if you’ve worked with C++ at all a little light bulb may have just gone off for you.  It works, even though it might be the very definition of programming by coincidence, so why is this so bad?

#

Why So Bad?

When calling a method in C# all parameters are passed by value.  It is a common misconception that reference types are passed by reference when they are infact passed by value.  This backs up the position of the C++ programmer, so what is the difference?

C# and its documentation has no concept of pointers (outside of IntPtr) when they are used extensively under the hood.  When you call a method and pass a reference type you are actually passing a pointer (that lives on the stack) to a memory location on the heap.    The pointer is copied so you can still manipulate the memory but you can’t affect the reference.  For example:

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

Has the output of:

Added by AddToSBPassedByRef

This could be confusing to a C++ developer because all classes, in C++, are created on the stack unless you declare a pointer and use new to create them on the heap.  Having said that a simple rule applies to both to C# and C++, if you have to use new it gets created on the heap and everything else is created on the stack.  Anything on the stack, value types or reference types, is copied between method calls.

Comments