Before C# 6, I would write code to dispose of an object like:
if (_odbcConnection != null)
{
_odbcConnection.Close();
_odbcConnection.Dispose();
_odbcConnection = null;
}
With 6, I can write much less code:
_odbcConnection?.Close();
_odbcConnection?.Dispose();
_odbcConnection = null;
But are the two equivalent?
Your two lower examples are almost equal. But the second block
_odbcConnection?.Close();
_odbcConnection?.Dispose();
_odbcConnection = null;
will be translated by the compiler to something like
var tmp1 = _odbcConnection;
if (tmp1 != null) tmp1.Close();
var tmp2 = _odbcConnection;
if (tmp2 != null) tmp2.Dispose();
_odbcConnection = null;
This means that this version is thread-safe, while the first (with the outer if
clause) is not. If some mysterious thread would set _odbcConnection
to null
after the if
but before Close()
or Dispose()
, a NullReferenceException
would be thrown.
By using the null-conditional-operator you avoid this problem, because the reference is first stored in a compiler generated variable and then checked and used.
The above translation only applies to fields and properties. For local variables (only in scope of a single method, e.g. method parameters), this translation is not necessary and the code ends up like
if (_odbcConnection != null) _odbcConnection.Dispose();
That is because local variables cannot be changed by different threads.
And of course this is only the generated C#. In IL you may not see this anymore as it is either optimized away or obsolete, because in IL the reference value is loaded into a register and then compared. Again, another thread can no longer change that value in the register. So on IL level this discussion is somewhat pointless.
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