Нулевой условный оператор и пустые методы

До C# 6 я писал бы код для избавления от объекта вроде:

if (_odbcConnection != null)
{
    _odbcConnection.Close();
    _odbcConnection.Dispose();
    _odbcConnection = null;
}

С 6 я могу написать гораздо меньше кода:

_odbcConnection?.Close();
_odbcConnection?.Dispose();
_odbcConnection = null;

Но эти два эквивалента?

1 ответ

Решение

Ваши два нижних примера почти равны. Но второй блок

_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(), NullReferenceException будет брошен.

Используя null-conditional-operator, вы избежите этой проблемы, потому что ссылка сначала сохраняется в переменной, сгенерированной компилятором, а затем проверяется и используется.


Приведенный выше перевод относится только к полям и свойствам. Для локальных переменных (только в рамках одного метода, например, параметров метода), этот перевод не требуется, и код заканчивается так:

if (_odbcConnection != null) _odbcConnection.Dispose();

Это потому, что локальные переменные не могут быть изменены разными потоками.

И конечно это только сгенерированный C#. В IL вы можете больше этого не видеть, поскольку он либо оптимизирован, либо устарел, потому что в IL эталонное значение загружается в регистр, а затем сравнивается. Опять же, другой поток больше не может изменять это значение в регистре. Так что на уровне IL это обсуждение несколько бессмысленно.

Другие вопросы по тегам