4

In another words, C# operator= will return the right value, isn't it? But C++ conventionally returns left value, right?

Rob Lao
  • 1,526
  • 4
  • 14
  • 38
  • 4
    I avoid this syntax precisely for this reason - it should be immediately obvious what your code does. – Justin Apr 08 '11 at 04:29
  • 1
    @Kragen: 1) It doesn't happen like that. 2) Even it did, what different behavior could *possibly* result? – Kirk Woll Apr 08 '11 at 04:30
  • @Kirk Dunno, but `a = c; b = c;` (when written on two lines) avoids even having to think about *any* possible ambiguity *and* makes it obvious that there are two assigments. – Justin Apr 08 '11 at 04:32
  • @Kragen, presumably two `=` suggests two assignments? (and for what it's worth, the comparison should be with `b = c; a = b`. – Kirk Woll Apr 08 '11 at 04:34
  • 1
    @Kirk What if A, B and C are **all** proprties and what if they are also different types with implictly overided casts - what getters / setters are called and what casts are invoked? I'm not saying that you can't work it out (or even that its difficult), just that *two simple assignments shouldn't involve this sort of mental overhead*. There are already 4 different answers to this question (one of which is wrong!) – Justin Apr 08 '11 at 04:45

3 Answers3

13

No, the result of the assignment expression is the value assigned, not the expression on the right. So consider this:

    byte a;
    int b;
    byte c = 10;

    a = b = c; // Fails to compile

This fails to compile because although b = c is valid, it's an expression of type int, which then can't be assigned to a which is of type byte.

From the C# 4 language spec, section 7.17.1:

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.

EDIT: Here's proof that it's the value which was assigned to b which is used, not the value of 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
    }
}

The a = b = c; statement is equivalent to:

BType tmp = c;
b = tmp;
a = tmp;

So the value of b isn't read afterwards (it's not equivalent to b = c; a = b;) - so if b were actually a property, the behaviour of the property would be irrelevant other than for side-effects. It's whatever value was used in the assignment to b which is also used in the assignment to a.

In this case, the assignment to b requires a conversion from ACType to BType which creates a BType with Value=50. The assignment to a then requires a conversion from BType to ACType, which creates an ACType with Value=25.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • ...although `a = b` is valid... You mean `b = a`? – Hemant Apr 08 '11 at 04:32
  • @Hemant: Sorry, it was broken. Fixed - and reversed to make it more like the original. – Jon Skeet Apr 08 '11 at 04:39
  • The interesting thing about this is the overall value of the expression `a = b = c` may look nothing at all like the value of *any* of the variables here, which may not at all be equal. Part of this I knew, part of this you helped me learn, and most of it I hope to never ever really have to consider in my coding activities. – Anthony Pegram Apr 08 '11 at 05:37
  • @Anthony: It's funny how this was today, and yesterday had another compound assignment question which wasn't as simple as it sounded: http://stackoverflow.com/questions/5577140/why-does-swapping-values-with-xor-fail-when-using-this-compound-form – Jon Skeet Apr 08 '11 at 05:47
  • Thanks. Now I *will* have nightmares. – Anthony Pegram Apr 08 '11 at 05:51
  • I wonder why it is so complex, and the result of the assignment is not the left operand. Such behaviour would be "symmetric" to function chain calling. – greenoldman May 05 '11 at 17:48
  • @macias: I don't think it would be terribly obvious for the property to be read again, to be honest. I would avoid code like this to start with though :) – Jon Skeet May 05 '11 at 18:27
  • @Jon Skeet, I rephrase -- for me such equivalent is (seriously) obvious -- b = c; a = b; (I refer to your last example). Why this is obvious for me? Because methods work this way, the last result is passed to next call as "this". After reading your explanation (thank you!) I simply gave up using assignment chaining, because of the principle of least surprise. – greenoldman May 05 '11 at 19:25
9

No, it works the same in both languagues: a = b = c; works like this: a = (b = c);. The assignment operator works right-to-left, and the value returned is the result of the assignment, which is the left-hand-side. So conceptually, a = b = c; is the same as: b = c; a = b;.

For C#, the assignment operators and the conditional operator (?:) are right-associative, meaning that operations are performed from right to left. For example, x = y = z is evaluated as x = (y = z). (Source)

Same for C++ (Source)

Andy White
  • 86,444
  • 48
  • 176
  • 211
  • I know the right operator= will be evaluated at first definitely in either C# or C++. My question actually is, for a = (b = c), which value will be used assign to 'a'? 'b' or 'c'? – Rob Lao Apr 08 '11 at 05:15
  • @Bob L: The value that was used to assign to `b`. I'm coming up with an example now. – Jon Skeet Apr 08 '11 at 05:19
  • you are contradicting yourself. In C# b will NOT be assigned to a! And you said it works the same in both languages (it is not true). "a = b = c; is the same as: b = c; a = b;" In C# it is not. – greenoldman May 05 '11 at 19:28
3

A useful tidbit about the semantics of a = b = c is that a will indeed receive the same value (normally c*) that was assigned to b, the present value of b is not a factor.

Yes, that could be considered surprising. Consider the example:

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? 

If the value passed through foo.Bar and the mutation follows into a, then a would be 5. However, that is not what happens. a and foo.Bar receive the same value, a does not receive the mutation from foo.Bar. It's not directly your question, but it's interesting enough to post considering some of the comments.


*Jon Skeet had an interesting observation about types that are implicitly convertible to one another but have data loss/modification in the conversion. In this case, the value may very well not be the original value of c, but the converted value, which may be different.

Anthony Pegram
  • 123,721
  • 27
  • 225
  • 246
  • Yes. I encountered something similar as your example though I used Indexer. The result of program doubts me so I posted this question. I am still learning C#, now I believe C#'s property and indexer are exceptions of returning left value of operator= rule... – Rob Lao Apr 08 '11 at 05:08
  • 1
    @Bob, consider the text posted in Jon Skeet's answer. *"The result of a simple assignment expression is the value **assigned** to the left operand."* It's not the value *of* the left operand, as that could have already mutated (as demonstrated above) and produce an unexpected result. – Anthony Pegram Apr 08 '11 at 05:11
  • 1
    It won't always be the same value as `c`. Imagine two types which have implicit conversions to each other, but lose data, where `a` and `c` are one type, and `b` is the other type. The conversion from the type of `c` to the type of `b` would be performed, then the conversion back to the type of `a` would be performed. – Jon Skeet Apr 08 '11 at 05:18
  • @Jon, interesting point. I'll indeed test that scenario where a data loss occurs prior to the assignment. – Anthony Pegram Apr 08 '11 at 05:20
  • 1
    @Jon... wow, yeah, just tested that. Might have a nightmare about this one. – Anthony Pegram Apr 08 '11 at 05:23
  • @Anthony: I've just posted a short but complete program in my answer. Is that roughly what you tested? Hopefully my explanation at the bottom covers your property part too. – Jon Skeet Apr 08 '11 at 05:25
  • @Jon, yes, that's basically what I did, using classes *and* structs to cover my bases. – Anthony Pegram Apr 08 '11 at 05:29