15

C# provides the ref and the out keyword to make arguments to be passed by reference. The semantic of the two is very similar. The only difference is in the initialization of the flaged variable:

  • ref requires the variable to be initialized before passing it to the function, out does not.
  • out requires the variable to be initialized inside the function, ref does not.

The use cases of these two keywords are also almost the same, and their too frequent usage is I beleive considered a code smell (although there are valid use cases like the TryParse and TryGetValue patterns).

Because of this could someone explain, why there are two very similar tools in C# for so narrow use cases?

Also, on MSDN, it is stated that they have different run-time behavior:

Although the ref and out keywords cause different run-time behavior, they are not considered part of the method signature at compile time.

How is their run-time behavior different?

Conclusion

Both answers looks correct, thank you both. I accepted jmoreno's because it is more explicit.

  • My guess is they both are implemented the same way as ref in C++; to pointers to the object pointer (or primitive) in question. i.e. Int32.TryParse(myStr, out myInt) (C#) is "executed" the same way as int32_tryParse(myStr, &myInt) (C) would; the only difference being some constraints enforced by the compiler to prevent bugs. (I'm not gonna post this as an answer because I may be wrong about how this works behind the scenes, but this is how I envision it works [because it makes sense]) – Cole Johnson Feb 10 '15 at 14:55
10

At the time this question was asked, the MSDN article was subtly incorrect (it has since been corrected). Instead of "cause different behavior" it should be "requires different behavior".

In particular, the compiler enforces different requirements for the two keywords, even though the same mecanism (IL) is used to enable the behavior.

  • So my understanding is that the compiler checks for things like dereferencing an out parameter or not assigning a ref parameter, but the IL it emits is just a reference either way. Is that correct? – Andrew says Reinstate Monica Feb 10 '15 at 17:56
  • Btw, one might tell this from the fact that out T and ref T are rendered identically in other CLI languages such as VB.Net or C++/CLI. – ach Feb 10 '15 at 18:14
  • @AndrewPiliser: correct. Assuming that the code compiles either way (you don't try to dereference when you shouldn't), the IL would be identical. – jmoreno Feb 10 '15 at 18:23
4

here's an interesting writeup on the topic that may answer your question:

http://www.dotnetperls.com/ref

point of interest:

"The difference between ref and out is not in the Common Language Runtime, but is in the C# language itself."

update:

the senior developer at my work just corroborated @jmoreno's answer, so make sure you read it!.

  • So if I understand correctly there is no difference on the IL level, which implies that they can't have different run time behavior. This would contradict with what MSDN says. Interestin article nevertheless! – Gábor Angyal Feb 10 '15 at 13:14
  • @GáborAngyal you don't happen to have a link to the MSDN article by chance do you? It might be nice to have that here if opinions differ – Dan Beaulieu Feb 10 '15 at 13:17
  • Here you go: msdn.microsoft.com/en-gb/library/t3c3bfhx.aspx, but it was in my question all along :) – Gábor Angyal Feb 10 '15 at 13:18
  • 1
    What does string have to do with all this? – Robert Harvey Feb 10 '15 at 15:08
  • It's just a paraphrase from the article. Updated to remove second line.. – Dan Beaulieu Feb 10 '15 at 15:17
2

Runtime? Absolutely none. You can't overload a method with the same parameters differing only by ref or out keyword.

Try to compile this and you'll get a compile error "Method with same signature is already declared":

    private class MyClass
    {
        private void DoSomething(out int param)
        {
        }
        private void DoSomething(ref int param)
        {
        }
    }

To answer this question: "...why there are two very similar tools in C# for so narrow use cases?"

From a code readability and API standpoint there is a huge difference. As a consumer of the API I know that when "out" is used the API does not depend on the out parameter. As the API developer I prefer the "out" and use "ref" only when absolutely (RARELY!) necessary. See this reference for a great discussion:

https://stackoverflow.com/questions/1516876/when-to-use-ref-vs-out

Supporting info: I compiled the following method and disassembled it. I used the ref and out keywords (out in this example), but the assembly code did not change except for an address reference as I would expect:

    private class MyClass
    {
        internal void DoSomething(out int param)
        {
            param = 0;
        }
    }

00000000 push ebp
00000001 mov ebp,esp
00000003 push edi
00000004 push esi
00000005 push ebx
00000006 sub esp,34h
00000009 xor eax,eax
0000000b mov dword ptr [ebp-10h],eax
0000000e mov dword ptr [ebp-1Ch],eax
00000011 mov dword ptr [ebp-3Ch],ecx
00000014 mov dword ptr [ebp-40h],edx
00000017 cmp dword ptr ds:[008B1710h],0
0000001e je 00000025
00000020 call 6E6B601E
00000025 nop
param = 0;
00000026 mov eax,dword ptr [ebp-40h]
00000029 xor edx,edx
0000002b mov dword ptr [eax],edx
}
0000002d nop
0000002e lea esp,[ebp-0Ch]
00000031 pop ebx
00000032 pop esi
00000033 pop edi
00000034 pop ebp
00000035 ret

Am I reading the assembly correctly?

  • What you write is correct, but please note that it is not an evident implication that there is no runtime difference because this overload is not allowed. – Gábor Angyal Feb 12 '15 at 7:18
  • Ahh - good point! Can anyone disassemble the code above and prove me right or wrong? I'm terrible at reading assembly, but curious to know. – Shmoken Feb 13 '15 at 2:28
  • Okay - I disassembled the code using "out" and "ref" and I can't see any difference for my DoSomething method: – Shmoken Feb 13 '15 at 20:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.