Could someone please explain to me why the code shown below is valid in C# and executes the call to Console.WriteLine
?
using (null)
{
Console.WriteLine ("something is here")
}
It compiles into (finally block is shown). As you can see compiler decides not to execute the Dispose()
method and jumps to the endfinally
instruction.
IL_0013: ldnull
IL_0014: ceq
IL_0016: stloc.1
IL_0017: ldloc.1
IL_0018: brtrue.s IL_0021 // branches here and decide not to execute Dispose()
IL_001a: ldnull
IL_001b: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0020: nop
IL_0021: endfinally
However, if I run the following code, it will fail with a NullReferenceException
(which is expected):
((IDisposable)null).Dispose();
IL_0023: ldnull
IL_0024: callvirt instance void [mscorlib]System.IDisposable::Dispose()
Why does the first version compile? Why does the compiler decide not to execute Dispose()
? Are there any other cases when compiler may decide not to call Dispose()
in a using
block?
The null statement satisfies the syntax requirement in cases that do not need a substantive statement body. As with any other C statement, you can include a label before a null statement.
The C NULL is a special reserved pointer value that does not point to any valid data object. The SQL null value is a special value that is distinct from all non-null values and denotes the absence of a (non-null) value.
Yes. NULL evaluates to false, since C considers any non-zero value true and any zero value false. NULL is essentially the zero address and is treated as such in comparisons, and I believe would be promoted to an int for the boolean check.
NULL values are used to indicate that you could have a value, but you don't know what that value should be yet. They are placeholders until you finally collect the data needed to fill the table field with a real value. You should never confuse NULL values for zeros or blank strings. This is another misconception.
The language spec explicitly states (8.13) that the captured value is tested for null if necessary, i.e. the finally
is essentially (with caveats around non-nullable types)
if(tmp != null) tmp.Dispose();
I frequently use this to my advantage, for things that might be null, but when they aren't: need disposing. In fact, here's a useful scenario (manually enumerating IEnumerable
):
IEnumerable blah = ...; // note non-generic version
IEnumerator iter = blah.GetEnumerator();
using(iter as IDisposable)
{
// loop
}
as the non-generic version of IEnumerator
isn't necessarily IDisposable
, but when it is, should be disposed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With