Today someone was curious why C#'s using statement won't throw a NullReferenceException. They had a using statement that opened a registry key, but even if that key didn't exist and the return value was null, they didn't have to worry about a NullReferenceException being thrown.
Before we start to look at why the exception isn't thrown, its useful to think about why one might be thrown in the first place. Using is syntactic sugar for wrapping a call to Dispose() in a try ... finally block. Most articles about IDisposable will show that the code:
is essentially equivalent to
Lets look at a more real life code snippet. The program in question involved opening a registry key, and not getting a NullReferenceException when the key didn't exist. The following code sample prints "Got a null key" to the console, but does not throw.
If using really works as I said above, why doesn't this cause a NullReferenceException when its time to Dispose the key? There are a few possibilities.
Options 1 and 2 are clearly incorrect, so that leaves us with two choices. Either the people who created the RegistryKey class are very clever, and created a non-null registry key object that's overloaded to act like it's null in comparisons, or the C# compiler is doing some extra work. Opening up the RegistryKey class in ildasm quickly shows that there are no overloaded operators defined, so that leaves us only with option number 4.
To verify this, lets crack open the code that the C# compiler produces from the snippet above. Running it through ildasm yields:
Lets take a closer look. Lines IL_000 through IL_00f are the call to RegistryKey.OpenSubKey(). The result is stored in the first local variable. From IL_0010 to IL_001d is the try block that we expect to see, and the finally block starts at line IL_001f. If this was a straight call to IDisposable, I'd expect to see only a ldloc followed by a callvirt instruction. However, there are a few other instructions present. Especially interesting are lines IL_001f and IL_0020. What these lines are doing is checking to see if the first local variable (in this case our registry key) is null, and if so skipping to the end of the finally block. From looking at this disassembly, it turns out that the code the C# compiler generates more closely resembles:
What does this mean to you? Probably not much of anything (aside from a possibly interesting look at how the C# compiler generates code for the using statement). As long as your code doesn't rely on the value being used inside the using statement to be non-null, and also doesn't rely on using throwing a NullReferenceException if this value is null, you're code is fine. However, if either one of those conditions is true, you'd be better off fixing the offending code before you have a subtle bug to trace down later.