Null 条件运算符和 void 方法
Null conditional operator and void methods
在 C# 6 之前,我会编写代码来处理像这样的对象:
if (_odbcConnection != null)
{
_odbcConnection.Close();
_odbcConnection.Dispose();
_odbcConnection = null;
}
有了6,我可以写更少的代码:
_odbcConnection?.Close();
_odbcConnection?.Dispose();
_odbcConnection = null;
但这两者是等价的吗?
你下面的两个例子几乎相等。但是第二块
_odbcConnection?.Close();
_odbcConnection?.Dispose();
_odbcConnection = null;
会被编译器翻译成
var tmp1 = _odbcConnection;
if (tmp1 != null) tmp1.Close();
var tmp2 = _odbcConnection;
if (tmp2 != null) tmp2.Dispose();
_odbcConnection = null;
这意味着这个版本是 thread-safe,而第一个(带有外部 if
子句)不是。如果某个神秘线程将 _odbcConnection
设置为 null
在 if
之后但在 Close()
或 Dispose()
之前,则会抛出一个 NullReferenceException
。
通过使用 null-conditional-operator 可以避免这个问题,因为引用首先存储在编译器生成的变量中,然后进行检查和使用。
以上翻译仅适用于字段和属性。对于局部变量(仅在单个方法的范围内,例如方法参数),这种翻译是没有必要的,代码最终像
if (_odbcConnection != null) _odbcConnection.Dispose();
那是因为局部变量不能被不同的线程改变。
当然这只是生成的 C#。在 IL 中,您可能不会再看到它,因为它要么被优化掉了,要么已经过时了,因为在 IL 中,参考值被加载到寄存器中,然后进行比较。同样,另一个线程不能再更改寄存器中的值。所以在 IL 级别上,这个讨论有点毫无意义。
在 C# 6 之前,我会编写代码来处理像这样的对象:
if (_odbcConnection != null)
{
_odbcConnection.Close();
_odbcConnection.Dispose();
_odbcConnection = null;
}
有了6,我可以写更少的代码:
_odbcConnection?.Close();
_odbcConnection?.Dispose();
_odbcConnection = null;
但这两者是等价的吗?
你下面的两个例子几乎相等。但是第二块
_odbcConnection?.Close();
_odbcConnection?.Dispose();
_odbcConnection = null;
会被编译器翻译成
var tmp1 = _odbcConnection;
if (tmp1 != null) tmp1.Close();
var tmp2 = _odbcConnection;
if (tmp2 != null) tmp2.Dispose();
_odbcConnection = null;
这意味着这个版本是 thread-safe,而第一个(带有外部 if
子句)不是。如果某个神秘线程将 _odbcConnection
设置为 null
在 if
之后但在 Close()
或 Dispose()
之前,则会抛出一个 NullReferenceException
。
通过使用 null-conditional-operator 可以避免这个问题,因为引用首先存储在编译器生成的变量中,然后进行检查和使用。
以上翻译仅适用于字段和属性。对于局部变量(仅在单个方法的范围内,例如方法参数),这种翻译是没有必要的,代码最终像
if (_odbcConnection != null) _odbcConnection.Dispose();
那是因为局部变量不能被不同的线程改变。
当然这只是生成的 C#。在 IL 中,您可能不会再看到它,因为它要么被优化掉了,要么已经过时了,因为在 IL 中,参考值被加载到寄存器中,然后进行比较。同样,另一个线程不能再更改寄存器中的值。所以在 IL 级别上,这个讨论有点毫无意义。