运算符'='在C#中链接 - 当然这个测试应该通过?

And*_*tan 18 c# compiler-construction operators

我刚刚写了一个属性设置器,并且脑子里有一个关于为什么我们没有得到一个属性可能涉及操作符链接return的结果的脑波,即:set=

var a = (b.c = d);
Run Code Online (Sandbox Code Playgroud)

(为了清楚起见,我添加了括号 - 但在实践中没有区别)

我开始思考 - C#编译器在哪里导出a上面示例中赋值的值?

逻辑说它应该来自(b.c = d)操作的结果,但是因为用一种void set_blah(value)方法实现它不可能.

所以唯一的其他选择是:

  • b.c在分配后重新读取并使用该值
  • 再利用 d

  • 编辑(自从Eric回答和评论) - 还有第三个选项,就是C#所做的:b.c在任何转换发生后使用写入的值

现在,在我看来,正确阅读上面的代码行是

设置a到设定的结果b.cd

我认为这是一个合理的代码阅读 - 所以我想我会测试这是否确实是一个稍微做作的测试会发生什么 - 但问问自己是否认为它应该通过或失败:

public class TestClass
{
  private bool _invertedBoolean;
  public bool InvertedBoolean
  {
    get
    {
      return _invertedBoolean;
    }
    set
    {
      //don't ask me why you would with a boolean,
      //but consider rounding on currency values, or
      //properties which clone their input value instead
      //of taking the reference.
      _invertedBoolean = !value;
    }
  }
}

[TestMethod]
public void ExampleTest()
{
  var t = new TestClass();
  bool result;
  result = (t.InvertedBoolean = true);
  Assert.IsFalse(result);
}
Run Code Online (Sandbox Code Playgroud)

此测试失败.

对代码生成的IL的仔细检查表明,该true值被加载到堆栈中,用dup命令克隆,然后在两个连续的赋值中弹出两个值.

这种技术对于字段非常有效,但对于我来说,每个实际上都是一个方法调用的属性似乎非常天真,其中实际的最终属性值不能保证是输入值.

现在我知道很多人讨厌嵌套作业等等,但事实是语言允许你这样做,所以他们应该按预期工作.

也许我真的很厚,但对我来说这表明编译器(.Net 4顺便说一句)不正确地实现了这种模式.但那么我的期望/读取代码是不正确的?

Mar*_*ell 18

分配的结果x = {expr}定义从{expr}的计算的值.

§14.14.1简单分配(ECMA334 v4)

...简单赋值表达式的结果是赋给左操作数的值.结果与左操作数的类型相同,并且始终归类为值....

并注意分配的值已经评估值d.因此,这里的实现是:

var tmp = (TypeOfC)d;
b.c = tmp;
a = tmp;
Run Code Online (Sandbox Code Playgroud)

虽然我也希望启用优化后它会使用dup指令而不是局部变量.

  • @Andras:我认为你是误会.赋值(bc = d)的值不是d的值,它不是bc的值 - 部分原因是bc可能没有值; 它可能是一个只写属性.**赋值的值是分配给bc的值** (2认同)

Eri*_*ert 16

我觉得有趣的是,你的期望是疯狂的任务 - 即分配两个不同的值,因为其中一个是非常奇怪的属性,具有不寻常的行为 - 是理想的事态.

正如您所推断的那样,我们竭尽全力避免这种状态.这是一个很好的事情.当你说"x = y = z"时,如果可能的话我们应该保证x和y最终被分配相同的值 - 即z - 即使y是一些不具有价值的疯狂的东西你给它."x = y = z"在逻辑上应该类似于"y = z,x = z",除了z仅被评估一次.在分配给x时,y根本没有涉及到这个问题; 为什么要这样?

此外,当然,当做"x = y = z"时,我们不能始终"重用"y,因为y可能是只写属性.如果没有getter从中读取值,该怎么办?

此外,我注意到你说"这适用于字段" - 如果字段是易变的,则不会.您无法保证您指定的值是该字段在易失性字段中所采用的值.您无法保证从过去的volatile字段中读取的值现在是该字段的值.

有关此主题的更多想法,请参阅我的文章:

http://blogs.msdn.com/b/ericlippert/archive/2010/02/11/chaining-simple-assignments-is-not-so-simple.aspx

  • @Andras:你的信念似乎源于"a = c = d"的意思是"c = d,a = c".实际上它的作用类似于"c = d,a = d"."c"和"a"的作用是它们*被写成*; 这里什么都没有*读*"c". (2认同)
  • @Andras:我明白为什么你会这么想,但这样做会导致一些奇怪的事情.例如,假设bc是string类型的读写属性.假设你看到`string x = bc = null; 如果(x == null){`如果有条件的话没有*被击中会不会令人惊讶?看起来x应该始终在那里被指定为null.但是如果将bc实现为`get {return _c; 设置{_c =值?? "}}`我们按你的方式做了,然后x将是一个空字符串,而不是null! (2认同)