Null conditional operator and void methods
Asked Answered
U

1

5

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?

Unconformity answered 7/2, 2017 at 15:12 Comment(1)
Run the code that you've already written and see for yourself.Io
S
10

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.

Schoof answered 7/2, 2017 at 15:19 Comment(2)
is this translation documented somewhere? because I can't prove it by looking at the generated IL codeOutmarch
@SelmanGenç for local variables there will be no extra compiler generated tmp, because it's not possible that any other thread can access local variables. So my example translation only applies to fields or properties. I checked it with dotPeek. Check the decompiled sources, not the IL. The IL may completely ommit this as it loads the value into registers and therefor does not need extra variables anymore. It may be optimized away.Balderdash

© 2022 - 2024 — McMap. All rights reserved.