Humblecoder

Apprentice unit tester, expert rambler

Archive for the ‘Basics’ tag

Passing Reference Types Using Ref, Take Two

without 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.

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

Written by Will

March 16th, 2010 at 11:42 am

Posted in Uncategorized

Tagged with , ,

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 ,

Get Adobe Flash playerPlugin by wpburn.com wordpress themes