编译器结构- Operator '='c#中的链接-当然这个测试应该通过

本文关键字:测试 Operator 结构 链接 编译器 | 更新日期: 2023-09-27 18:01:55

我刚刚在写一个属性设置程序,突然想到为什么当一个属性可能涉及到操作符=链时,我们不需要return set的结果,即:

var a = (b.c = d);

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

我开始思考- c#编译器在哪里导出在上面的例子中分配给a的值?

逻辑上说它应该来自(b.c = d)操作的结果,但由于这是用void set_blah(value)方法实现的,所以它不能。

所以唯一的其他选项是:

  • 在赋值后重新读取b.c并使用该值
  • 重复使用d

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

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

set 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);
}

此测试失败

仔细检查为代码生成的IL,可以看到true值被加载到堆栈上,用dup命令克隆,然后在两个连续的赋值中弹出。

这种技术对于字段来说是完美的,但是对于每个属性实际上都是一个方法调用,并且实际的最终属性值不能保证是输入值的属性来说,这种技术似乎非常幼稚。

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

也许我真的很笨,但对我来说,这表明编译器对这种模式的实现是不正确的。净4倍)。但是,我对代码的期望/阅读是否不正确?

编译器结构- Operator '='c#中的链接-当然这个测试应该通过

赋值x = {expr}的结果是定义从{expr}求值

§14.14.1简单赋值(ECMA334 v4)

…简单赋值表达式的结果就是赋给的值左操作数。结果与左操作数具有相同的类型,并且总是被归类为一个值。…

并注意分配给的值是值已经从d中求出。因此这里的实现是:

var tmp = (TypeOfC)d;
b.c = tmp;
a = tmp;

,虽然我也希望优化启用它将使用dup指令,而不是一个局部变量。

我发现有趣的是,您的期望是疯狂的赋值——即赋两个不同的值,因为其中一个是具有不寻常行为的极其奇怪的属性——是理想的状态。

正如你所推断的,我们尽我们所能避免这种状态。这是一件好的事情。当你说"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

赋值操作符用于返回第二个操作数的求值结果(在本例中为b)。它也将这个值赋给它的第一个操作数,这个赋值是通过调用一个返回void的方法来完成的。

规格说明:

14.14.1简单赋值

=操作符称为简单赋值操作符。在简单赋值操作中,右操作数应是可隐式转换为该类型的类型表达式左操作数的。该操作分配该权限的值属性给出的变量、属性或索引器元素的操作数左操作数。简单赋值表达式的结果是赋给左操作数的值。结果具有相同的类型

为左操作数,且总是被分类为值。

实际情况是:

  1. d被求值(让我们把产生的值称为val)
  2. 结果赋值给b.c
  3. 赋值运算符的结果是val
  4. a被赋值val
  5. 第二个赋值操作符的结果也是val(但是因为整个表达式在这里结束,所以它没有被使用)