【问题标题】:Null conditional operator and void methods空条件运算符和 void 方法
【发布时间】:2017-06-24 21:37:48
【问题描述】:

在 C# 6 之前,我会编写代码来处理对象,例如:

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

使用 6,我可以编写更少的代码:

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

但是这两者是等价的吗?

【问题讨论】:

  • 运行你已经写好的代码,自己看看。

标签: c# c#-6.0 null-conditional-operator


【解决方案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 子句)不是。如果某个神秘的线程在if 之后,Close()Dispose() 之前将_odbcConnection 设置为null,则会抛出NullReferenceException

通过使用空条件运算符可以避免这个问题,因为引用首先存储在编译器生成的变量中,然后检查并使用。


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

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

那是因为局部变量不能被不同的线程改变。

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

【讨论】:

  • 这个翻译记录在某处吗?因为我无法通过查看生成的 IL 代码来证明这一点
  • @SelmanGenç 对于局部变量,不会生成额外的编译器tmp,因为任何其他线程都不可能访问局部变量。所以我的示例翻译仅适用于字段或属性。我用dotPeek检查了它。检查反编译的源代码,而不是 IL。 IL 可以完全忽略这一点,因为它将值加载到寄存器中,因此不再需要额外的变量。它可能会被优化掉。
猜你喜欢
  • 2017-12-01
  • 2020-11-29
  • 2017-08-25
  • 2016-06-08
  • 1970-01-01
  • 2015-03-28
  • 2016-09-07
  • 1970-01-01
相关资源
最近更新 更多