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

时间:2016-12-13 10:21:35

标签: c# short-circuiting null-conditional-operator

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为null,则根本不执行任何操作。如果refTypeInstance不为null,则执行语句)

更新(结论):

  • 无法在赋值语句的左侧使用空条件运算符,因为这会违反赋值语句的表达式树的预期求值逻辑(使赋值操作短路而根本不执行它) )
  • 理想的解决方案是一个新的条件赋值运算符(仅当赋值的左侧不为空时才执行),实质上是一个'如果不是空的一个赋值一个衬里'

2 个答案:

答案 0 :(得分:4)

您期望的行为:

  

(即如果refTypeInstance为null,则根本不执行任何操作。如果refTypeInstance不为null,则执行语句)

由于操作员的工作方式,这是不可能的。更具体地说,您遇到了运算符优先级的问题以及如何基于以下形式创建表达式树:

声明

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

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

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

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

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

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

或者换句话说:只要<?php define('SOME_DIR', ''); $fileName = 'file'; if (! file_exists ( SOME_DIR . $fileName.'.txt' )) { echo "not there"; //so save normally } else{ $files = glob(SOME_DIR.$fileName.'_*.txt'); $counter = count($files)+1; echo "there"; $fileName = $fileName."_" .$counter.".txt"; echo $fileName; // save with number at the end } 只是将运算符的评估短接到表达式树的分支,就会认为这是预期的行为。但是在这种情况下,这会改变运算符在表达式树中更高的行为,你绝对不会期望。

答案 1 :(得分:1)

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

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

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