Ваши два нижних примера: почти равный. Но второй блок
_odbcConnection?.Close();
_odbcConnection?.Dispose();
_odbcConnection = null;
будет переведена компилятором на что-то вроде
var tmp1 = _odbcConnection;
if (tmp1 != null) tmp1.Close();
var tmp2 = _odbcConnection;
if (tmp2 != null) tmp2.Dispose();
_odbcConnection = null;
Это означает, что эта версия является поточно-безопасным, в то время как первый (с внешней if
п) не является. Если какая-то таинственная нить установит _odbcConnection
на null
после if
, но до Close()
или Dispose()
, a NullReferenceException
будет выброшена.
Используя оператор с нулевым условием, вы избегаете этой проблемы, поскольку эта ссылка сначала сохраняется в сгенерированной переменной компилятора, а затем проверяется и используется.
Указанные переводы относятся только к полям и свойствам. Для локальных переменных (только в рамках одного метода, например, параметры метода), этот перевод не является необходимым, и код заканчивается, как
if (_odbcConnection != null) _odbcConnection.Dispose();
Это потому, что локальные переменные не могут быть изменены различными потоками.
И, конечно же, это только сгенерированный C#. В IL вы можете не видеть этого больше, поскольку он либо оптимизирован, либо устарел, потому что в IL контрольное значение загружается в регистр, а затем сравнивается. Опять же, другой поток больше не может изменять это значение в регистре. Таким образом, на уровне ИЛ эта дискуссия несколько бессмысленна.
Запустите код, который вы уже написали, и убедитесь сами. – Servy