运算符'=' C# 中的链接 - 这个测试肯定应该通过吗?

发布于 2024-11-19 09:46:37 字数 1675 浏览 2 评论 0 原文

我刚刚编写了一个属性设置器,突然想到为什么当运算符 < 中可能涉及属性时,我们不必返回 set 的结果。 code>= 链接,即:(

var a = (b.c = d);

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

我开始思考 - C# 编译器从哪里导出分配给 a< 的值/code> 在上面的例子中?

从逻辑上讲,它应该来自 (bc = d) 操作的结果,但由于它是使用 void set_blah(value) 方法实现的,所以不可能。

因此,唯一的其他选择是:

  • 赋值后重新读取 bc 并使用该值
  • 重新使用 d

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

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

a设置为将bc设置为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 命令进行克隆,然后两个值均弹出连续的任务。

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

现在我知道很多人讨厌嵌套作业等,但事实是该语言允许您执行它们,因此它们应该按预期工作。

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

I was just writing a property setter and had a brain-wave about why we don't have to return the result of a set when a property might be involved in operator = chaining, i.e:

var a = (b.c = d);

(I've added the brackets for clarity - but it makes no difference in practise)

I started thinking - where does the C# compiler derive the value that is assigned to a in the above example?

Logic says that it should be from the result of the (b.c = d) operation but since that's implemented with a void set_blah(value) method it can't be.

So the only other options are:

  • Re-read b.c after the assignment and use that value
  • Re-use d

  • Edit (since answered and comments from Eric) - there's a third option, which is what C# does: use the value written to b.c after any conversions have taken place

Now, to my mind, the correct reading of the above line of code is

set a to the result of setting b.c to d

I think that's a reasonable reading of the code - so I thought I'd test whether that is indeed what happens with a slightly contrived test - but ask yourself if you think it should pass or fail:

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

This test fails.

Closer examination of the IL that is generated for the code shows that the true value is loaded on to the stack, cloned with a dup command and then both are popped off in two successive assignments.

This technique works perfectly for fields, but to me seems terribly naive for properties where each is actually a method call where the actual final property value is not guaranteed to be the input value.

Now I know many people hate nested assignments etc etc, but the fact is the language lets you do them and so they should work as expected.

Perhaps I'm being really thick but to me this suggests an incorrect implementation of this pattern by the compiler (.Net 4 btw). But then is my expectation/reading of the code incorrect?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

暖阳 2024-11-26 09:46:37

赋值x = {expr}的结果定义从{expr}计算的值

§14.14.1 简单赋值 (ECMA334 v4)

...
简单赋值表达式的结果是分配给的值
左操作数。结果与左操作数具有相同的类型,
并且始终被分类为一个值。
...

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

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

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

The result of an assignment x = {expr} is defined as the value evaluated from {expr}.

§14.14.1 Simple assignment (ECMA334 v4)

...
The result of a simple assignment expression is the value assigned to
the left operand. The result has the same type as the left operand,
and is always classified as a value.
...

And note that the value assigned is the value already evaluated from d. Hence the implementation here is:

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

although I would also expect with optimisations enabled it will use the dup instruction rather than a local variable.

我最亲爱的 2024-11-26 09:46:37

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

正如您所推断的,我们会尽一切努力避免这种状态。这是一件好事。当你说“x = y = z”时,如果可能的话,我们应该保证x和y最终分配相同的值——z的值——即使y是一些疯狂的东西,不包含你的值给它。 “x = y = z”在逻辑上应该类似于“y = z, x = z”,只不过 z 只计算一次。当赋值给 x 时,y 根本不涉及问题;为什么要这样呢?

另外,当然,当执行“x = y = z”时,我们不能始终如一地“重用”y,因为 y 可能是一个只写属性。如果没有 getter 来读取值怎么办?

另外,我注意到你说“这适用于字段”——而不是如果该字段是易失性的,则它不会。如果该字段是易失性字段,则您无法保证您分配的值就是该字段所采用的值。您无法保证过去从易失性字段中读取的值就是该字段现在的值。

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

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

I find it interesting that your expectation is that the crazy assignment -- that is, assigning two different values because one of them is an extremely weird property with unusual behaviour -- is the desirable state of affairs.

As you've deduced, we do everything in our power to avoid that state. That is a good thing. When you say "x = y = z" then if at all possible we should guarantee that x and y end up assigned the same value -- that of z -- even if y is some crazy thing that doesn't hold the value you give it. "x = y = z" should logically be like "y = z, x = z", except that z is only evaluated once. y doesn't come into the matter at all when assigning to x; why should it?

Also, of course when doing "x = y = z" we cannot consistently "reuse" y because y might be a write only property. What if there is no getter to read the value from?

Also, I note that you say "this works for fields" -- not if the field is volatile it doesn't. You have no guarantee whatsoever that the value you assigned is the value that the field takes on if it is a volatile field. You have no guarantee that the value you read from a volatile field in the past is the value of the field now.

For more thoughts on this subject, see my article:

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

违心° 2024-11-26 09:46:37

赋值运算符被记录为返回计算第二个操作数(在本例中为 b)的结果。没关系,它还会将此值分配给其第一个操作数,并且此分配是通过调用返回 void 的方法来完成的。

规范说:

14.14.1 简单赋值

= 运算符称为简单赋值运算符。在简单的赋值中,正确的操作数应
是可隐式转换为该类型的类型的表达式
的左操作数。该操作对右边的值进行赋值
由给定的变量、属性或索引器元素的操作数
左操作数。简单赋值表达式的结果是
分配给左操作数的值。结果的类型与
左操作数,并且始终被分类为值。

因此,实际上发生的情况是:

  1. d 进行求值(我们将生成的值称为 val),
  2. 结果分配给 bc
  3. 赋值运算符的结果是 < code>val
  4. a 被赋予值 val
  5. 第二个赋值运算符的结果也是 val (但由于整个表达式在这里结束,它未使用)

The assignment operator is documented to return the result of evaluating its second operand (in this case, b). It doesn't matter that it also assigns this value to its first operand, and this assignment is done by calling a method that returns void.

The spec says:

14.14.1 Simple assignment

The = operator is called the simple assignment operator. In a simple assignment, the right operand shall
be an expression of a type that is implicitly convertible to the type
of the left operand. The operation assigns the value of the right
operand to the variable, property, or indexer element given by the
left operand. The result of a simple assignment expression is the
value assigned to the left operand. The result has the same type as
the left operand, and is always classified as a value.

So actually what happens is:

  1. d is evaluated (let's call the value produced val)
  2. the result is assigned to b.c
  3. the assignment operator's result is val
  4. a is assigned the value val
  5. the second assignment operator's result is also val (but since the whole expression ends here, it goes unused)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文