简体   繁体   中英

Inconsistency in C# language syntax

I'm learning C# and came across few things that made me little uncomfortable about the its syntax.

Case 1

byte num1 = 10;    // works
int ten = 10;
byte num2 = ten;   // Compile error: Cannot implicitly convert 'int' to byte. An explicit conversion exists.

In first statement compiler implicitly casts literal 10 (of type int) to byte, conversely it does not do same in third statement.

Case 2

int[] numbers1 = { 10, 20, 30 };     // works
int[] numbers2;
numbers2 = { 10, 20, 30 };          // Compiler error: Invalid expression term: {

The above shortened array-initializer does not work in every statement.

And there may be many more such inconsistencies...

It seems the error-versions are right because those are as per the defined syntax, and non-error-versions (for similar cases) are language created constructs to just make the language easy to code in.

But still shouldnt it be consistent in every place we use it?

Case 1 is a special case covered explicitly by the C# Language Specification .

From §6.1.9 Implicit constant expression conversions :

An implicit constant expression conversion permits the following conversions:

• A constant-expression (§7.19) of type int can be converted to type sbyte, byte, short, ushort, uint, or ulong, provided the value of the constant-expression is within the range of the destination type.

• A constant-expression of type long can be converted to type ulong, provided the value of the constant-expression is not negative.

Because the variable ten is not declared as const then the above rule does not apply, and no implicit conversion is allowed and you get a compile error.

Note that if you change ten to be const int ten = 10; then it will work because, of course, it is now a constant.

Case 2 is enabled by a feature of the new operator.

From §1.8 Arrays :

The new operator permits the initial values of the array elements to be specified using an array initializer, which is a list of expressions written between the delimiters { and }.

The following example allocates and initializes an int[] with three elements.

int[] a = new int[] {1, 2, 3};

Note that the length of the array is inferred from the number of expressions between { and }. Local variable and field declarations can be shortened further such that the array type does not have to be restated.

int[] a = {1, 2, 3};

Observe how it allows you to omit the new keyword, even though it is actually using the new operator behind the scenes.

So because this syntactic sugar is provided by the new operator, it can only be used when implicitly using new .

In your example, you can still partially use this syntax when you separate declaration and initialisation, but you must then explicitly use the new keyword:

int[] numbers2;
numbers2 = new [] { 10, 20, 30 };

Now you could argue that the compiler could allow the syntax without new , since it knows the type of numbers2 and could infer that the presence of a { and } means that it must be an array initialisation. However, that would require a new rule and I imagine that the language designers didn't think that it would be used enough to justify adding it.

In case1, you are initializing with a literal. This

byte b = 10;  

is ok because the literal "10" is a valid byte. Assigning a variable to another variable rather than a literal is not the same thing. In the next example, imagine if we did this:

int ten = 10;
ten = 100000; // evil
byte b = ten;

The first case only worked because C# was pretty nice by trying its best to interpret the "10" literal as a byte. It can do that because it has the value available at compile time, and it's 0..255. When assigning the value from ten it can't know the value is limited to 0..255 so it has to fail or silently truncate. In the case of C#, the behavior is to fail.

In case 2 you are using the "array initializer syntax". It's a bit odd that this is only usable for array initializers and not generally as an expression. I'm actually unsure why that is, perhaps because of grammar ambiguity. Contrary to what you might think, the expression {1, 2, 3} is not shorthand for new int[] {1, 2, 3} , it only works the same when used in an initializer.

If you want to do what you (perhaps) intended in Case 2 you would have to use the full (non-initializer) expression:

int[] numbers1 = new int[] { 10, 20, 30 };     
int[] numbers2;
numbers2 = new int[] { 20, 30, 40 };

Edit: As pointed out in a comment there is a shorthand for non-initializer array creation: you can drop the array type like so, and only do new[] {10, 20, 30} in cases where the type can be inferred from the values.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM