Currently I'm teaching a class of C++ programmers the basics of the C# language. As we discussed the topic operators I used C# standard categories of primary, unary etc. operators.
One of the attendees felt puzzled, because in the C# standard the "postfix ++/--" have been put in the category of primary operators rather than the "prefix ++/--". Her rationale behind this confusion was, that she would rather implement the C++ operator "postfix ++/--" in terms of the operator "prefix ++/--". In other words she would rather count the operator "prefix ++/--" as a primary operator. - I understand her point, but I can't give to her a rationale behind that. OK the operators "postfix ++/--" have a higher precedence than "prefix ++/--", but is this the only rationale behind that?
The spec mentioned it in section "14.2.1 Operator precedence and associativity".
So my very neutral question: Why are Postfix ++/-- categorized as primary Operators in C#?
Is there a deeper truth in it?
Since the ECMA standard itself does not define what a 'Primary' operator is, other than order of precedence (i.e. coming before 'Unary') there can be no other significance. The choice of words was probably bad.
Take into account that in many C-link languages, postfix operators tend to create a temporary variable where the expression's intermediate result is stored (see: "Prefer prefix operators over postfix" at Semicolon). Thus, they are fundamentally different from the prefix version.
Nonetheless, quickly checking how Mono and Visual Studio compile for-loops using the postfix and prefix forms, I saw that the IL code produced is identical. Only if you use the postfix/prefix expression's value does it translate to different IL (only affecting where the 'dup' instruction in placed), at least with those implementations mentioned.
EDIT: Okay, now I'm back home, I've removed most of the confusing parts...
I don't know why x++
is classified as a primary expression but ++x
isn't; although I doubt it makes much difference in terms of the code you would write. Ditto precedence. I wonder whether the postfix is deemed primary as it's used more commonly? The annotated C# specs don't have any annotations around this, by the way, in either the ECMA edition or the Microsoft C# 4 editions. (I can't immediately find my C# 3 edition to check.)
However, in terms of implementation, I would think of ++
as a sort of pseudo-operator which is used by both prefix and postfix expressions. In particular, when you overload the ++
operator, that overload is used for both postfix and prefix increment. This is unlike C++, as stakx pointed out in a comment.
One thing to note is that while a post-increment/post-decrement expression has to have a primary expression as an operand, a pre-increment/pre-decrement expression only has to have a unary expression as an operand. In both cases the operand has to be classified as a variable, property access or indexer access though, so I'm not sure what practical difference that makes, if any.
EDIT: Just to give another bit of commentary, even though it seems arbitrary, I agree it does seem odd when the spec states:
Primary expressions include the simplest forms of expressions
But the list of steps for pre-increment is shorter/simpler than list of steps for post-increment (as it doesn't include the "save the value" step).
the difference is that
a[i++] will access the element indexed i.
a[++i] will access teh element indexed i+1.
In both cases after execution of a[++i/i++]
i will be i+1.
This can make troubles because you can't make assumption on parameters order
function(i++,i++,i++)
will increment i 3 times but you don't know in wich order. if initially i is 4 you can also have
function(4,5,6)
but also function(6,5,4)
or also function(6,4,5).
and that is still nothing because I used as example native types (for example "int"), things get worse when you have classes.
When overloading the operator result is not changed, what is changed is it's precedence. and this too can cause troubles.
So in one case "++" is applied before returning the reference, in the other case is applied "after" returning the reference. And when you overload it probably is better having it applied before returnin the reference (so ++something is much better than something++ at least from overloading point of view.)
take a generic class with overloaded ++ (of wich we have 2 items, foo and bar)
foo = bar ++; //is like writing (foo=bar).operator++();
foo = ++bar; // is like writing foo= (bar.operator++());
and there's much difference. Especially when you just don't assign your reference but do something more complex with it, or internally your object has stuff that has to do with shallow-copies VS deep copies.