C# Null条件运算符的替代方案(条件赋值)?

4
C#的null条件运算符允许有用的短路操作: C# null-conditional operator
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为空,则不执行任何操作。如果refTypeInstance不为空,则执行该语句。
更新(结论):
- 空值条件运算符不能用于赋值语句左侧,因为这会违反赋值表达式树的预期求值逻辑(跳过赋值并完全不执行它)。 - 理想的解决方案是一种新的条件赋值运算符(仅在赋值的左侧不为空时才执行),本质上是“如果不为空一次赋值一行”的一种简化写法。

4
“那么就什么也不做”对读者来说可能不太明显。因此这是一个有争议的功能,我不想要它。 - H H
2
我认为当你开始在任何地方都使用?.运算符时,它会成为可读性较差的不良实践。 - Fabio
2
并不是所有的事情都需要捷径。 - Patrick Hofman
2
实际上,它是被支持的,但仅适用于方法调用而不是属性/字段赋值。因此,(unit as RangedUnit)?.SetPreferredTarget(UnitType.Melee);会起作用(如果您使用方法替换属性设置器)。 - Ivan Stoev
3
有时候我认为人们试图用空值条件运算符来隐藏他们的错误,这与在所有地方使用 try{..}catch(Exception){} 的做法是一样的,往往你会在其他地方发现这个错误。甚至最坏的情况是客户也会注意到它。 - Tim Schmelter
显示剩余8条评论
2个回答

4
您期望的行为是:
(即如果refTypeInstance为空,则什么也不做。如果refTypeInstance不为空,则执行语句)
由于运算符的工作方式,这是不可能的。更具体地说,您在运算符优先级方面有问题,以及基于此形成表达式树的方式:
在语句中
(unit as RangeUnit).PreferredTarget = UnitType.Melee;

赋值运算符(=)位于表达式树的根部,左右表达式作为分支。
当左侧表达式在赋值之前被求值时,会发生NullReferenceException异常。此时编译器已经开始评估=。由于解引用运算符(.)会在运行时抛出NullReferenceException异常,因此编译器可以安全地继续解析表达式树。
另一方面,如果允许这个语句:
(unit as RangeUnit)?.PreferredTarget = UnitType.Melee;

如果编译器需要检查refTypeInstance的值是否为null,它可以这样做。但问题是,编译器会怎样处理当前正在遍历的表达式树?它不能像第一个例子那样继续执行,因为它必须丢弃表达式树中的=.。它基本上必须插入两个解析树的选择,一个当?.的左侧为null时,另一个当不为null时。然而,这将改变控制流,这绝对不是你从运算符中期望的行为。
换句话说,只要?.短路其表达式树分支的运算符评估,你就会认为这是一种预期的行为。但在这种情况下,这将改变表达式树中更高级别的运算符的行为,这肯定不是你所期望的。

1
然而,这将是对控制流的更改,这绝对不是您从运算符期望的。是这样吗?就我所知,foo?.DoSomething()会修改控制流;存在两个执行分支:如果foonull则不执行任何操作,否则调用DoSomething() - InBetween
@InBetween:是的,但它可以跳出表达式,这与使用 &&|| 进行短路评估基本相同。在 OPs 的示例中不能这样做,因为它会使 = 在运算符堆栈上悬而未决。 - Sefe
1
我并不是在争论这个。我争论的是.?实际上改变了控制流,因为它本质上是一个伪装成if-else的语句。你最后一段的措辞是误导性的。 - InBetween
@Fortmann:你说得对,我更新了我的帖子来纠正这个问题。最终关键在于如何评估表达式树。 - Sefe
1
是的,让我们等待 ?= 运算符 ;) - Sefe
显示剩余5条评论

1
因为它们并不做同样的事情,
如果单位为null(或不是远程单位),第一个片段将返回null。
如果在尝试设置某物时发生这种情况,您将无法将null设置为值(并且会出现错误)。

@TimSchmelter - 哦,是的,谢谢。 - Sayse
是的,这是有意为之的。第一个片段旨在介绍和描述空条件运算符作为入口点,然而问题的其余部分所描述的请求可能涉及另一种解决方案(因此在问题标题中包含“替代”一词)。 - Fortmann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接