Humblecoder

Apprentice unit tester, expert rambler

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

with 3 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.

</rant>

VN:F [1.9.3_1094]
Rating: 0.0/5 (0 votes cast)

Written by Will

March 12th, 2010 at 11:20 pm

Posted in Uncategorized

Tagged with ,

3 Responses to 'C# Basics: Ref’ing References, Ref’ing Hell'

Subscribe to comments with RSS or TrackBack to 'C# Basics: Ref’ing References, Ref’ing Hell'.

  1. Hi Will,

    [I'm not 100% on what you are trying to prove, so I may have missed the point, but...]

    You are correct, in that the original code you are looking at doesn’t need the “ref”. But I’m not sure the ‘proof’ is quite right either.

    When you pass using the “ref” (or “out”) keyword, you are converting a reference into “reference to a reference” – if you are familiar with C/C++, you can pretty much substitute the word “reference” with “pointer”.

    The reason your code is behaving the way it is, is because you are changing the reference inside the two test functions. In both examples, you are assigning a “new StringBuilder()” to a *local* reference (parameters variables are *local*).

    This would work exactly the same in C/C++, but you’d just have added two memory leaks.

    [Comment out line 16, and line 22, re-run your demo and you'll see what I mean.]

    You use “ref” if you want to update a reference outside of the scope of the function you are in. Exactly the same as you do in C/C++.

    The original code is just wrong; even in C++ it is wrong.

    Hope this makes sense, or have I missed the point of you post?!

    regards,
    RichS

    VA:F [1.9.3_1094]
    Rating: 0.0/5 (0 votes cast)

    RichS

    16 Mar 10 at 08:25

  2. Rereading the post I don’t think I’ve been very clear.

    I was trying to show the danger of passing reference types by ref. The example is meant to show that if you pass a reference type, StringBuilder, using ref then the callee can do new and the caller will get the new reference. Meaning any other state changes have been lost. This could lead to some difficult to track down bugs.

    If ref isn’t used then you can still access the object’s memory but you can’t change the reference for the caller.

    (if that makes sense, I need to think of a more succinct way of putting it)

    VN:F [1.9.3_1094]
    Rating: 0.0/5 (0 votes cast)

    Will

    16 Mar 10 at 09:45

  3. [...] my last post I talked about by passing reference types using the ref keyword but it didn’t make a lot of [...]

Leave a Reply

Get Adobe Flash playerPlugin by wpburn.com wordpress themes