I'd like to elaborate on a specific point Eric Lippert made in his answer and put the spotlight on a particular occasion that hasn't at all been touched upon by anyone else. Eric said:
[...] an assignment almost always leaves behind the value that was just assigned in a register.
I'd like to say that the assignment will always leave behind the value we tried to assign to our left operand. Not just "almost always". But I don't know because I haven't found this issue commented in the documentation. It might theoretically be a very effective implemented procedure to "leave behind" and not reevaluate the left operand, but is it efficient?
'Efficient' yes for all the examples so far constructed in the answers of this thread. But efficient in the case of properties and indexers that use get- and set accessors? Not at all. Consider this code:
class Test
{
public bool MyProperty { get { return true; } set { ; } }
}
Here we have a property, which isn't even a wrapper for a private variable. Whenever called upon he shall return true, whenever one tries to set his value he shall do nothing. Thus whenever this property is evaluated, he shall be truthy. Let's see what happens:
Test test = new Test();
if ((test.MyProperty = false) == true)
Console.WriteLine("Please print this text.");
else
Console.WriteLine("Unexpected!!");
Guess what it prints? It prints Unexpected!!
. As it turns out, the set accessor is indeed called, which does nothing. But thereafter, the get accessor is never called at all. The assignment simply leaves behind the false
value we tried to assign to our property. And this false
value is what the if statement evaluates.
I'll finish off with a real world example that got me researching this issue. I made an indexer which was a convenient wrapper for a collection (List<string>
) that a class of mine had as a private variable.
The parameter sent to the indexer was a string, which was to be treated as a value in my collection. The get accessor would simply return true or false if that value existed in the list or not. Thus the get accessor was another way to use the List<T>.Contains
method.
If the indexer's set accessor was called with a string as an argument and the right operand was a bool true
, he would add that parameter to the list. But if the same parameter was sent to the accessor and the right operand was a bool false
, he would instead delete the element from the list. Thus the set accessor was used as a convenient alternative to both List<T>.Add
and List<T>.Remove
.
I thought I had a neat and compact "API" wrapping the list with my own logic implemented as a gateway. With the help of an indexer alone I could do many things with a few set of keystrokes. For instance, how can I try to add a value to my list and verify that it's in there? I thought this was the only line of code necessary:
if (myObject["stringValue"] = true)
; // Set operation succeeded..!
But as my earlier example showed, the get accessor which is supposed to see if the value really is in the list wasn't even called. The true
value was always left behind effectively destroying whatever logic I had implemented in my get accessor.
while ((s = GetWord()) != null) Process(s);
is not). – Alexanderalexandr