Why was the statement (j++); forbidden?
The following code is wrong (see it on ideone):
public class Test
{
public static void Main()
{
int j = 5;
(j++); // if we remove the "(" and ")" then this compiles fine.
}
}
error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement
- Why does the code compile when we remove the parentheses?
- Why does it not compile with the parentheses?
- Why was C# designed that way?
Deep insights appreciated.
I shall do my best.
As other answers have noted, what's going on here is the compiler is detecting that an expression is being used as a statement. In many languages -- C, JavaScript, and many others -- it is perfectly legal to use an expression as a statement. 2 + 2;
is legal in these languages, even though this is a statement that has no effect. Some expressions are useful only for their values, some expressions are useful only for their side effects (such as a call to a void returning method) and some expressions, unfortunately, are useful for both. (Like increment.)
Point being: statements that consist only of expressions are almost certainly errors unless those expressions are typically thought of as more useful for their side effects than their values. C# designers wished to find a middle ground, by allowing expressions that were generally thought of as side-effecting, while disallowing those that are also typically thought of as useful for their values. The set of expressions they identified in C# 1.0 were increments, decrements, method calls, assignments, and somewhat controversially, constructor invocations.
ASIDE: One normally thinks of an object construction as being used for the value it produces, not for the side effect of the construction; in my opinion allowing new Foo();
is a bit of a misfeature. In particular, I've seen this pattern in real-world code that caused a security defect:
catch(FooException ex) { new BarException(ex); }
It can be surprisingly hard to spot this defect if the code is complicated.
The compiler therefore works to detect all statements that consist of expressions that are not on that list. In particular, parenthesized expressions are identified as just that -- parenthesized expressions. They are not on the list of "allowed as statement expressions", so they are disallowed.
All of this is in service of a design principle of the C# language. If you typed (x++);
you were probably doing something wrong. This is probably a typo for M(x++);
or some just thing. Remember, the attitude of the C# compiler team is not "can we figure out some way to make this work?" The attitude of the C# compiler team is "if plausible code looks like a likely mistake, let's inform the developer". C# developers like that attitude.
Now, all that said, there actually are a few odd cases where the C# specification does imply or state outright that parentheses are disallowed but the C# compiler allows them anyways. In almost all those cases the minor discrepancy between the specified behaviour and the allowed behaviour is completely harmless, so the compiler writers have never fixed these small bugs. You can read about those here:
Is there a difference between return myVar vs. return (myVar)?
In the C# language specification
Expression statements are used to evaluate expressions. Expressions that can be used as statements include method invocations, object allocations using the new operator, assignments using = and the compound assignment operators, increment and decrement operations using the ++ and -- operators and await expressions.
Putting parentheses around a statement creates a new so-called parenthesized expression. From the specification:
A parenthesized-expression consists of an expression enclosed in parentheses. ... A parenthesized-expression is evaluated by evaluating the expression within the parentheses. If the expression within the parentheses denotes a namespace or type, a compile-time error occurs. Otherwise, the result of the parenthesized-expression is the result of the evaluation of the contained expression.
Since parenthesized expressions are not listed as a valid expression statement, it is not a valid statement according to the specification. Why the designers chose to do it this way is anyone's guess but my bet is because parentheses do no useful work if the entire statement is contained in parentheses: stmt
and (stmt)
are exactly the same.
beacause the brackets around the i++
are creating/defining an expression.. as the error message says.. a simple expression cant be used as a statement.
why the language was designed to be this way? to prevent bugs, having misleading expressions as statements, that produce no side effect like having the code
int j = 5;
j+1;
the second line has no effect(but you may not have noticed). But instead of the compiler removing it(because the code is not needed).it explicitly asks you to remove it(so you will be aware or the error) OR fix it in case you forgot to type something.
edit:
to make the part about bracked more clear.. brackets in c# (besides other uses, like cast and function call), are used to group expressions, and return a single expression (make of the sub expressions).
at that level of code only staments are allowed.. so
j++; is a valid statement because it produces side effects
but by using the bracked you are turning it into as expression
myTempExpression = (j++)
and this
myTempExpression;
is not valid because the compiler cant asure that the expression as side effect.(not without incurring into the halting problem)..