Нулевой условный оператор и пустые методы
До 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 это обсуждение несколько бессмысленно.