4

C# 空条件运算符允许有用的短路:

double? range = (unit as RangedUnit)?.WeaponRange;

不幸的是,空条件运算符不能以相同的方式用于简写赋值,因为它返回一个值(不能用于左手赋值):

(unit as RangedUnit)?.PreferredTarget = UnitType.Melee;

导致可能的替代语法:

if (unit is RangedUnit)
{
    (unit as RangedUnit).PreferredTarget = UnitType.Melee;
}

如果编译器知道 RangedUnit 是引用类型(不是值类型),为什么不能有条件地执行速记语法

refTypeInstance?.SomeField = value;

(即如果 refTypeInstance 为 null,则什么也不做。如果 refTypeInstance 不为 null,则执行该语句)

更新(结论):

  • 空条件运算符不能用于赋值语句的左侧,因为这会违反赋值语句表达式树的预期评估逻辑(使赋值操作短路并且根本不执行它)
  • 理想的解决方案是一个新的条件赋值运算符(仅在赋值的左侧不为空时执行),本质上是“如果不为空,则一个赋值一个衬里”
4

2 回答 2

4

您期望的行为:

(即如果 refTypeInstance 为 null,则什么也不做。如果 refTypeInstance 不为 null,则执行该语句)

由于操作员的工作方式,这是不可能的。更具体地说,您对运算符优先级以及如何基于此形成表达式树有疑问:

在声明中

(unit as RangeUnit).PreferredTarget = UnitType.Melee;

赋值运算符 ( =) 将位于表达式树的根部,左右表达式作为分支。

ANullReferenceException将在评估左手时(分配之前)发生。此时编译器已经开始评估=. 由于解引用运算符 ( .) 将在运行时抛出 a NullReferenceException,因此编译器可以安全地继续解析表达式树。

另一方面,如果该声明允许:

(unit as RangeUnit)?.PreferredTarget = UnitType.Melee;

...编译器必须发出代码来检查 的值是否refTypeInstance为空。它可以做到这一点,但问题是,编译器将如何处理它当前正在经历的表达式树?它不能像第一个例子那样简单地继续下去,因为它必须丢弃=表达式树的向上和树的.向下。它基本上必须插入两种解析树的替代方法,一种是左侧?.null,另一种是不是。然而,这将改变控制流,这绝对不是您对操作员的期望。

或者换一种说法:只要?.将运算符的求值短路到其表达式树的分支上,您就会认为这是一种预期的行为。但是在这种情况下,这会改变表达式树中更高级别的运算符的行为,这是您绝对不会想到的。

于 2016-12-13T10:44:05.667 回答
1

因为他们没有做同样的事情,

如果 unit 为 null(或不是范围单位),则第一个片段将返回 null。

如果在您尝试设置某些内容时发生这种情况,那么您将无法将 null 设置为 value(并且最终会出现错误)。

于 2016-12-13T10:24:17.903 回答