2017-02-07 20 views
1

Перед C# 6 Я хотел бы написать код, чтобы избавиться от объекта, как:Null условного оператор и пустотные методы

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

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

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

Но являются двумя эквивалент?

+3

Запустите код, который вы уже написали, и убедитесь сами. – Servy

ответ

6

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

_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 контрольное значение загружается в регистр, а затем сравнивается. Опять же, другой поток больше не может изменять это значение в регистре. Таким образом, на уровне ИЛ эта дискуссия несколько бессмысленна.

+0

Этот перевод документирован где-то? потому что я не могу это доказать, посмотрев на сформированный код IL –

+0

@ SelmanGenç для локальных переменных не будет никакого генерируемого компилятором 'tmp', потому что невозможно, чтобы любой другой поток мог обращаться к локальным переменным. Поэтому мой пример перевода применяется только к полям или свойствам. Я проверил его с помощью dotPeek. Проверьте _decompiled sources_, а не IL. IL может полностью исключить это, поскольку он загружает значение в регистры и для этого больше не нужны дополнительные переменные. Он может быть оптимизирован. –