空条件运算符和void方法

The*_*der 2 c# c#-6.0 null-conditional-operator

在C#6之前,我会编写代码来处理像这样的对象:

if (_odbcConnection != null)
{
    _odbcConnection.Close();
    _odbcConnection.Dispose();
    _odbcConnection = null;
}
Run Code Online (Sandbox Code Playgroud)

有了6,我可以编写更少的代码:

_odbcConnection?.Close();
_odbcConnection?.Dispose();
_odbcConnection = null;
Run Code Online (Sandbox Code Playgroud)

但这两个是等价的吗?

Ren*_*ogt 6

你的两个较低的例子几乎是相同的.但第二块

_odbcConnection?.Close();
_odbcConnection?.Dispose();
_odbcConnection = null;
Run Code Online (Sandbox Code Playgroud)

将由编译器翻译成类似的东西

var tmp1 = _odbcConnection;
if (tmp1 != null) tmp1.Close();
var tmp2 = _odbcConnection;
if (tmp2 != null) tmp2.Dispose();
_odbcConnection = null;
Run Code Online (Sandbox Code Playgroud)

这意味着此版本是线程安全的,而第一个(使用外部if子句)则不是.如果某种神秘的线程将设置_odbcConnectionnullif,但之前Close()还是Dispose(),一个NullReferenceException会被抛出.

通过使用null-conditional-operator,可以避免此问题,因为引用首先存储在编译器生成的变量中,然后进行检查和使用.


以上翻译仅适用于字段和属性.对于局部变量(仅在单个方法的范围内,例如方法参数),此转换不是必需的,代码最终会像

if (_odbcConnection != null) _odbcConnection.Dispose();
Run Code Online (Sandbox Code Playgroud)

这是因为不同的线程不能改变局部变量.

当然这只是生成的C#.在IL中,您可能不会再看到它,因为它被优化掉或过时,因为在IL中,参考值被加载到寄存器中然后进行比较.同样,另一个线程不能再在寄存器中更改该值.所以在IL级别上,这种讨论有点毫无意义.