编译器结构- 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.c
为d
的结果
我认为这是对代码的合理解读——所以我想我应该用一个稍微做作的测试来测试一下是否确实如此——但是问问你自己,你认为它应该通过还是失败:
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倍)。但是,我对代码的期望/阅读是否不正确?
赋值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简单赋值
=操作符称为简单赋值操作符。在简单赋值操作中,右操作数应是可隐式转换为该类型的类型表达式左操作数的。该操作分配该权限的值属性给出的变量、属性或索引器元素的操作数左操作数。简单赋值表达式的结果是赋给左操作数的值。结果具有相同的类型
为左操作数,且总是被分类为值。
实际情况是:
-
d
被求值(让我们把产生的值称为val
) - 结果赋值给
b.c
- 赋值运算符的结果是
val
-
a
被赋值val
- 第二个赋值操作符的结果也是
val
(但是因为整个表达式在这里结束,所以它没有被使用)