Welcome to Atalasoft Community Sign in | Help

Out, Out, Damn Ref

What is the difference between the out and ref keywords in C#?  Rick and I were discussing this earlier today and I decided that I wanted to find out for real.  Inherently, I know what the difference is: ref is passed by reference and out is pass by reference with the added requirement that the parameter must be assigned to.

But this is a very minor semantic difference, so the real question is whether or not the difference is enforced by the language/compiler or by the CLR.  For this, we will determine the answer empirically with this chunk of code:

private void SetMyFriend(out int x)
{
    x = 5;
}

private void TouchMyFriend(ref int x)
{
    x = 3;
}

private void MyFriend()
{
    int x;
    SetMyFriend(out x);
    TouchMyFriend(ref x);
}

 Once this is written and compiled, we can pull it up with ildasm and have a look at what the compiler has done.

Here is the IL for SetMyFriend()

.method private hidebysig instance void  SetMyFriend([out] int32& x) cil managed
{
  // Code size       5 (0x5)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldarg.1  // put address of x on the stack
  IL_0002:  ldc.i4.5 // put a 5 on the stack
  IL_0003:  stind.i4 // store the 5 into the address
  IL_0004:  ret
} // end of method BasicTesting::SetMyFriend

No surprises - x is passed in as an int32& or a reference to a four byte int - ah, but it has the magic [out] attribute on it as well.

Here is TouchMyFriend():

.method private hidebysig instance void  TouchMyFriend(int32& x) cil managed
{
  // Code size       5 (0x5)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldarg.1
  IL_0002:  ldc.i4.3
  IL_0003:  stind.i4
  IL_0004:  ret
} // end of method BasicTesting::TouchMyFriend

You'll notice that except for the [out] in the declaration, it is absolutely identical to SetMyFriend.  I won't insert the calling code, but it is precisely the same for both.  So the answer to the question is that the compiler enforces it to the degree that it will attempt to determine if the value has been assigned via static analysis.  Yet, is this 100% the case because the parameter is marked as [out].  Does the CLR do the verification?  The answer is no - if you write the following managed C++ method:

static void DontTouchMe([System::Runtime::InteropServices::Out]int __gc &x)
{
}

the C++ compiler lets it go by and at runtime, the CLR lets it work too.  This is moderately distressing, but not surprising.  Lesson: out is only fully honored in C#.
Published Friday, April 18, 2008 3:44 PM by Steve Hawley

Comments

Tuesday, April 22, 2008 4:22 PM by DotNetKicks.com

# Out, Out, Damn Ref

You've been kicked (a good thing) - Trackback from DotNetKicks.com

Thursday, April 24, 2008 9:11 AM by senfo

# re: Out, Out, Damn Ref

There is one other important difference that wasn't mentioned.  If you reverse the order in which the two methods are called, the code will not compile.  This is because the out keyword allows you to pass a variable that have not already been assigned to.  A ref argument, on the other hand, requires that  the variable be initialized ahead of time.

Tuesday, July 07, 2009 11:26 AM by Steve's Tech Talk

# How I Learned to Start Worrying and Distrust the Bomb

This is a post about a serious bug I turned up in the Microsoft C++ compilers that target CLI (both Managed

Anonymous comments are disabled