tags:

views:

659

answers:

11

This is allowed:

int a, b, c;
a = b = c = 16;

string s = null;
while ((s = "Hello") != null) ;

To my understanding, assignment s = ”Hello”; should only cause “Hello” to be assigned to s, but the operation shouldn’t return any value. If that was true, then ((s = "Hello") != null) would produce an error, since null would be compared to nothing.

What is the reasoning behind allowing assignment statements to return a value?

+13  A: 

For one, it allows you to chain your assignments, as in your example:

a = b = c = 16;

For another, it allows you to assign and check a result in a single expression:

while ((s = foo.getSomeString()) != null) { /* ... */ }

Both are possibly dubious reasons, but there are definitely people who like these constructs.

Michael Burr
+1 Chaining isn't a critical feature but it's nice to see it "just works" when so many things don't.
Mike Burton
+26  A: 

Haven’t you provided the answer? It’s to enable exactly the kinds of constructs you have mentioned.

A common case where this property of the assignment operator is used is reading lines from a file...

string line;
while ((line = streamReader.ReadLine()) != null)
    // ...
Timwi
+1 This has to be one of the most practical uses of this construct
Mike Burton
I really like it for really simple property initializers, but you have to be careful and draw your line for "simple" low for readability: `return _myBackingField ?? (_myBackingField = CalculateBackingField());` A lot less chaff than checking for null and assigning.
Thomas G. Mayfield
+4  A: 

If assignment didn't return a value, the line a = b = c = 16 wouldn't work either.

Also being able to write things like while ((s = readLine()) != null) can be useful sometimes.

So the reason behind letting assignment return the assigned value, is to let you do those things.

sepp2k
+1  A: 

For the two reasons you include in your post
1) so you can do a = b = c = 16
2) so you can test if an assignment succeeded if ((s = openSomeHandle()) != null)

JamesMLV
+3  A: 

I think you're misunderstanding how the parser is going to interpret that syntax. The assignment will be evaluated first, and the result will then be compared to NULL, i.e. the statement is equivalent to:

s = "Hello"; //s now contains the value "Hello"
(s != null) //returns true.

As others have pointed out, the result of an assignment is the assigned value. I find it hard to imagine the advantage to having

((s = "Hello") != null)

and

s = "Hello";
s != null;

not be equivalent...

djacobson
While you are right from a practical sense that your two lines are equivalent your statement that the assignment is not returning a value is not technically true. My understanding is that the result of an assignment is a value (per the C# spec), and in that sense is no different from say the addition operator in an expression like 1 + 2 + 3
Steve Ellinger
Point taken, and fixed!
djacobson
+2  A: 

I think the main reason is the (intentional) similarity with C++ and C. Making the assigment operator (and a lot of other language constructs) behave like their C++ counterparts just follows the principle of least surprise, and any programmer coming from another curly-bracket language can use them without spending much thought. Being easy to pick up for C++ programmers was one of the main design goals for C#.

tdammers
+9  A: 

Aside from the reasons already mentioned (assignment chaining, set-and-test within while loops, etc), to properly use the using statement you need this feature:

using (Font font3 = new Font("Arial", 10.0f))
{
    // Use font3.
}

MSDN discourages declaring the disposable object outside of the using statement, as it will then remain in scope even after it has been disposed (see the MSDN article I linked).

Martin Törnwall
I don't think this is really assignment chaining per se.
kenny
@kenny: Uh... No, but I never claimed it was, either. As I said, *aside* from the reasons already mentioned -- which includes assignment chaining -- the using statement would be a lot more difficult to use were it not for the fact that the assignment operator returns the result of the assignment operation. This isn't really related to assignment chaining at all.
Martin Törnwall
But as Eric noted above, "assignment statements do not return a value". This is actually just language syntax of the using statement, proof to that is that one can't use an "assignment expression" in a using statement.
kenny
@kenny: Apologies, my terminology was clearly wrong. I do realize that the using statement could be implemented without the language having general support for assignment expressions returning a value. That would, in my eyes, be terribly inconsistent though.
Martin Törnwall
+24  A: 

To my understanding, assignment s = "Hello"; should only cause "Hello" to be assigned to s, but the operation shouldn’t return any value.

Your understanding is 100% incorrect. Can you explain why you believe this false thing?

What is the reasoning behind allowing assignment statements to return a value?

First off, assignment statements do not return a value. Assignment expressions return a value. An assignment expression is a legal statement; there are only a handful of expressions which are legal statements in C#: instance construction, increment, decrement, invocation and assignment expressions may be used where a statement is expected.

There is only one kind of expression in C# which does not produce some sort of value, namely, an invocation of something that is typed as returning void. Every other kind of expression produces a value or variable or reference or property access or event access, and so on.

Notice that all the expressions which are legal as statements are useful for their side effects. That's the key insight here, and I think perhaps the cause of your intuition that assignments should be statements and not expressions. Ideally, we'd have exactly one side effect per statement, and no side effects in an expression. It is a bit odd that side-effecting code can be used in an expression context at all.

The reasoning behind allowing this feature is because (1) it is frequently convenient and (2) it is idiomatic in C-like languages.

One might note that the question has been begged: why is this idiomatic in C-like languages?

You'd have to ask Dennis Richie to be sure, but my guess is that an assignment almost always leaves behind the value that was just assigned in a register. C is a very "close to the machine" sort of language. It seems plausible and in keeping with the design of C that there be a language feature which basically means "keep on using the value that I just assigned". It is very easy to write a code generator for this feature; you just keep on using the register that stored the value that was assigned.

Eric Lippert
+1 for interesting pondering
Mike Burton
Wasn't aware that anything which returns a value ( even instance construction is considered an expression )
and thank you all for your help
@user437291: If instance construction wasn’t an expression, you couldn’t do anything with the constructed object — you couldn’t assign it to something, you couldn’t pass it into a method, and you couldn’t call any methods on it. Would be pretty useless, wouldn’t it?
Timwi
+1  A: 

The fact that 'a++' or 'printf("foo")' may be useful either as a self-contained statement or as a part of a larger expression means that C has to allow for the possibility that expression results may or may not be used. Given that, there's a general notion that expressions which might usefully 'return' a value may as well do so. Assignment chaining can be slightly "interesting" in C, and even more interesting in C++, if all the variables in question do not have precisely the same type. Such usages are probably best avoided.

supercat
+1  A: 

My favorite use of assignment expressions is for lazily initialized properties.

private string _name;
public string Name
{
    get { return _name ?? (_name = ExpensiveNameGeneratorMethod()); }
}
Nathan Baulch
A: 

An extra advantage I don't see given in the answers here, is that the syntax for assignment is based on arithmetic.

Now x = y = b = c = 2 + 3 means something different in arithmetic than a C-style language; in arithmetic its an assertion, we state that x is equal to y etc. and in a C-style language it's an instruction that makes x equal to y etc. after it is executed.

This said, there's still enough relation between the arithmetic and the code that it doesn't make sense to disallow what is natural in the arithmetic unless there's a good reason. (The other thing that C-style languages took from the use of the equals symbol is the use of == for equality comparison. Here though because the right-most == returns a value this sort of chaining would be impossible.)

Jon Hanna