C# Null 条件运算符替代方案(条件赋值)?
C# Null Conditional Operator alternative (conditional assignment)?
C# null-conditional operator 允许有用的短路:
double? range = (unit as RangedUnit)?.WeaponRange;
不幸的是,空条件运算符不能以相同的方式用于简写赋值,因为它 returns 一个值(不能用于左手赋值):
(unit as RangedUnit)?.PreferredTarget = UnitType.Melee;
导致可能的替代语法:
if (unit is RangedUnit)
{
(unit as RangedUnit).PreferredTarget = UnitType.Melee;
}
如果编译器知道RangedUnit是引用类型(不是值类型),为什么不能有条件地执行shorthand语法
refTypeInstance?.SomeField = value;
(即如果 refTypeInstance 为 null,则什么都不做。如果 refTypeInstance 不为 null,则执行该语句)
更新(结论):
- 空条件运算符不能用在赋值语句的左侧,因为这会违反赋值语句表达式树的预期求值逻辑(短路赋值操作而不执行它)
- 理想的解决方案是新的条件赋值运算符(仅在赋值的左侧不为空时执行),本质上是 'if not null one assignment one liner'
因为他们做的事情不一样,
如果单位为空(或不是远程单位),第一个片段将 return 为空。
如果在您尝试设置某些内容时发生这种情况,那么您将无法将 null 设置为值(并且您最终会遇到错误)。
您期望的行为:
(i.e. If refTypeInstance is null, then simply do nothing. If refTypeInstance is not null, then execute the statement)
由于运营商的工作方式,这是不可能的。更具体地说,您遇到了 运算符优先级 以及如何基于此形成表达式树的问题:
在声明中
(unit as RangeUnit).PreferredTarget = UnitType.Melee;
赋值运算符(=
)位于表达式树的根部,左右表达式为分支。
A NullReferenceException
会在评估左手时(赋值前)出现。此时编译器已经开始计算=
。由于取消引用运算符 (.
) 将在运行时抛出 NullReferenceException
,编译器继续解析表达式树是安全的。
另一方面,如果允许此语句:
(unit as RangeUnit)?.PreferredTarget = UnitType.Melee;
...编译器必须发出代码来检查 refTypeInstance
的值是否为空。它可以做到这一点,但问题是,编译器将如何处理它当前正在处理的表达式树?它不能像第一个示例那样简单地继续,因为它必须丢弃表达式树上的 =
和树下的 .
。它基本上必须插入两种解析树的选择,一种是 ?.
的左边是 null
,另一种不是。然而,这将改变控制流,这绝对不是您对操作员的期望。
或者换句话说:当 ?.
只是将运算符的计算短路到其表达式树的分支下时,您会认为这是预期的行为。但在这种情况下,这会改变表达式树 更高层 运算符的行为,这是您绝对不会想到的。
C# null-conditional operator 允许有用的短路:
double? range = (unit as RangedUnit)?.WeaponRange;
不幸的是,空条件运算符不能以相同的方式用于简写赋值,因为它 returns 一个值(不能用于左手赋值):
(unit as RangedUnit)?.PreferredTarget = UnitType.Melee;
导致可能的替代语法:
if (unit is RangedUnit)
{
(unit as RangedUnit).PreferredTarget = UnitType.Melee;
}
如果编译器知道RangedUnit是引用类型(不是值类型),为什么不能有条件地执行shorthand语法
refTypeInstance?.SomeField = value;
(即如果 refTypeInstance 为 null,则什么都不做。如果 refTypeInstance 不为 null,则执行该语句)
更新(结论):
- 空条件运算符不能用在赋值语句的左侧,因为这会违反赋值语句表达式树的预期求值逻辑(短路赋值操作而不执行它)
- 理想的解决方案是新的条件赋值运算符(仅在赋值的左侧不为空时执行),本质上是 'if not null one assignment one liner'
因为他们做的事情不一样,
如果单位为空(或不是远程单位),第一个片段将 return 为空。
如果在您尝试设置某些内容时发生这种情况,那么您将无法将 null 设置为值(并且您最终会遇到错误)。
您期望的行为:
(i.e. If refTypeInstance is null, then simply do nothing. If refTypeInstance is not null, then execute the statement)
由于运营商的工作方式,这是不可能的。更具体地说,您遇到了 运算符优先级 以及如何基于此形成表达式树的问题:
在声明中
(unit as RangeUnit).PreferredTarget = UnitType.Melee;
赋值运算符(=
)位于表达式树的根部,左右表达式为分支。
A NullReferenceException
会在评估左手时(赋值前)出现。此时编译器已经开始计算=
。由于取消引用运算符 (.
) 将在运行时抛出 NullReferenceException
,编译器继续解析表达式树是安全的。
另一方面,如果允许此语句:
(unit as RangeUnit)?.PreferredTarget = UnitType.Melee;
...编译器必须发出代码来检查 refTypeInstance
的值是否为空。它可以做到这一点,但问题是,编译器将如何处理它当前正在处理的表达式树?它不能像第一个示例那样简单地继续,因为它必须丢弃表达式树上的 =
和树下的 .
。它基本上必须插入两种解析树的选择,一种是 ?.
的左边是 null
,另一种不是。然而,这将改变控制流,这绝对不是您对操作员的期望。
或者换句话说:当 ?.
只是将运算符的计算短路到其表达式树的分支下时,您会认为这是预期的行为。但在这种情况下,这会改变表达式树 更高层 运算符的行为,这是您绝对不会想到的。