C#中的a=b=c;是否等同于b=c; a=c;,而C++中是b=c; a=b;?

4
换句话说,C#的operator=会返回右值,对吧?但是传统的C++会返回左值,对吗?

4
我避免使用这种语法,正是因为这个原因——你的代码应该立即清晰明了。 - Justin
1
@Kragen:1)不会发生那样的事情。2)即使发生了,可能会产生什么不同的行为? - Kirk Woll
@Kirk 不知道,但是 a = c; b = c;(写在两行上)甚至避免了考虑任何可能的歧义,并且明显表明有两个赋值。 - Justin
@Kragen,大概两个=表示两个赋值?(顺便说一下,比较应该是b = c; a = b。) - Kirk Woll
1
@Kirk 如果A、B和C都是属性,而且它们也是不同类型的,具有隐式覆盖转换——那么会调用哪些getter/setter并触发哪些转换?我并不是说你不能解决这个问题(甚至不是说这很困难),只是两个简单的赋值不应该涉及这种心理负担。对于这个问题已经有4个不同的答案了(其中一个是错误的!) - Justin
3个回答

13

不是的,赋值表达式的结果是被赋值的值,而不是右侧的表达式。因此请考虑以下代码:

    byte a;
    int b;
    byte c = 10;

    a = b = c; // Fails to compile

这段代码编译失败的原因是,尽管 b=c 是合法的表达式,但它是一个类型为 int 的表达式,无法被赋值给类型为 byte 的变量 a

这里引用了 C# 4 语言规范,第 7.17.1 节:

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

编辑后:以下是证明所赋值的是 b 的值而不是 c 的值的证据:

using System;

class ACType
{
    public int Value { get; set; }

    public static implicit operator BType(ACType ac)
    {
        return new BType { Value = ac.Value / 2 };
    }
}

class BType
{
    public int Value { get; set; }

    public static implicit operator ACType(BType b)
    {
        return new ACType { Value = b.Value / 2 };
    }
}

class Test
{
    static void Main()
    {
        ACType a, c;
        BType b;

        c = new ACType { Value = 100 };
        a = b = c;

        Console.WriteLine(a.Value); // Prints 25
    }
}

a = b = c;语句等同于:

BType tmp = c;
b = tmp;
a = tmp;
所以,b 的值在此后并没有被“读取”(它与 b = c; a = b; 不等价),因此如果 b 实际上是一个属性,属性的行为除了副作用之外都是无关紧要的。在对 a 赋值时使用的是同样用于对 b 赋值时使用的那个值。

在这种情况下,对 b 的赋值需要将 ACType 转换为 BType,从而创建一个 Value=50 的 BType。然后对 a 的赋值需要将 BType 转换为 ACType,从而创建一个 Value=25 的 ACType

@Hemant:抱歉,它出了问题。已修复-并反转以使其更像原始版本。 - Jon Skeet
有趣的是,表达式a = b = c的总体值可能看起来与这里的任何变量的值都不一样,这些变量可能根本不相等。其中一部分我知道,一部分是你帮助我学习的,而大部分我希望在我的编码活动中永远不必考虑。 - Anthony Pegram
我想知道为什么它这么复杂,而且赋值的结果不是左操作数。这种行为将与函数链调用“对称”。 - greenoldman
@macias:老实说,我觉得重新读取属性并不是非常明显的事情。不过,我建议一开始就避免使用这样的代码 :) - Jon Skeet
@Jon Skeet,我重新表述一下——对我来说,这个等价的写法(真的)很明显——b = c; a = b;(我指的是你最后一个例子)。为什么对我来说这很明显?因为方法就是这样工作的,上一个结果会作为“this”传递给下一个调用。在阅读了您的解释之后(谢谢!),我放弃了使用赋值链的想法,因为这违背了最小惊讶原则。 - greenoldman
显示剩余3条评论

9
不,它在两种语言中的工作方式相同:a = b = c; 的工作方式如下:a = (b = c);。赋值运算符从右至左工作,并返回的值是赋值的结果,即左侧。因此,在概念上,a = b = c;b = c; a = b; 相同。
对于C#,赋值运算符和条件运算符(?:)是右结合的,这意味着操作是从右到左执行的。例如,x = y = z 被评估为 x = (y = z)。(来源
C++也是如此。(来源

我知道在C#或C++中,正确的operator=肯定会首先被评估。我的问题实际上是,对于a =(b = c),将使用哪个值分配给'a'? 'b'还是'c'? - Rob Lao
@Bob L:用于赋值给b的值。我现在正在想一个例子。 - Jon Skeet
你自相矛盾了。在C#中,b不会被赋值给a!而你说它在两种语言中的工作方式是相同的(这是不正确的)。"a = b = c;"在C#中并不等同于"b = c; a = b;"。 - greenoldman

3

关于 a = b = c 的语义,一个有用的提示是,a 确实会接收到与 b 赋值相同的值(通常是 c*),而 b 目前的值并不影响。

是的,这可能令人惊讶。考虑以下示例:

class Foo
{
    private int _bar;
    public int Bar
    {
        get { return _bar; }
        set { _bar = value + 1; }
    }
}

...

Foo foo = new Foo();
int a, c;
c = 4;

a = foo.Bar = c; // is 'a' 4 or 5? 

如果通过 foo.Bar 传递的值并且变异进入 a,则 a 将是 5。然而,事实并非如此。 afoo.Bar 收到相同的值,a 没有收到来自 foo.Bar 的变异。这不是直接回答问题,但考虑到一些评论,这非常有趣。
*Jon Skeet 对隐式转换为彼此可转换但在转换中具有数据丢失/修改的类型提出了一个有趣的观察。在这种情况下,该值很可能不是 c 的原始值,而是转换后的值,可能会有所不同。

是的,我遇到了与你的例子类似的问题,尽管我使用了索引器。程序的结果让我感到困惑,所以我发了这个问题。我仍在学习C#,现在我相信C#的属性和索引器是返回operator=规则左值的例外情况... - Rob Lao
1
@Bob,请参考Jon Skeet答案中发布的文本。 “简单赋值表达式的结果是分配给左操作数的值。”它不是左操作数的值,因为它可能已经变化(如上所示),从而产生意外的结果。 - Anthony Pegram
1
它不会始终是与 c 相同的值。想象一下两种类型彼此具有隐式转换但会丢失数据,其中 ac 是一种类型,而 b 是另一种类型。将从 c 的类型转换为 b 的类型,然后再将其转换回 a 的类型。 - Jon Skeet
1
@Jon... 哇,是的,刚刚测试了一下。可能会做关于这个的噩梦。 - Anthony Pegram
@Anthony:我在我的回答中刚刚发布了一个简短但完整的程序。这大概是你测试过的吗?希望我底部的解释也涵盖了你的属性部分。 - Jon Skeet
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接