A conditional statement that mixes a wrapped type and a constant integer expression

I get what I don't expect in C # when I combine a narrower zero and wider numerical value as the second and third operands of the conditional operator. This DOES NOT WORK, but I find it if the wider numerical value is a constant expression of type int and the narrower NULL value is of type SByte? or Int16 ?. Demo video:

bool test = true; Int16? aShort = 5; Int32 anInt = 5; const Int32 aConstInt = 4; Object o1 = test ? aShort : anInt; // does not compile Object o2 = test ? aShort : aConstInt; // does compile 

My question is, why does it compile if my int is constant? And I cannot find a link to this in the C # language specification, what happens here?

+4
source share
4 answers

The C # 4.0 language specification in ยง7.14 (Conditional statement) states the following:

The second and third operands x and y the ?: Operator control the type of the conditional expression.

If x is of type x and y is of type y , then

  • If an implicit conversion (ยง6.1) exists from x to y but not from y to x , then y is a conditional expression type.
  • If an implicit conversion (ยง6.1) exists from y to x but not from x to y , then x is a conditional expression type.
  • Otherwise, the type of expression cannot be determined, and a compile-time error occurs.

And in ยง6.1.9 (Implicit Constant Expression Conversions) the following is stated:

The implicit conversion of constant expressions allows the following transformations:

  • A constant expression (ยง7.19) of type int can be converted to type sbyte , byte , short , ushort , uint or ulong , provided that 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 if the value of the constant expression is not negative.

As you can see, constant expressions like int and long handled specially.

So the expression test ? aShort : aConstInt test ? aShort : aConstInt true, since there is an implicit conversion from an expression of an int 4 constant to short , and then to short? (and therefore the expression type is short? ), but test ? aShort : anInt test ? aShort : anInt invalid because there is no implicit conversion from a non-constant expression of type int to short? or from short? to int .

+4
source

When you write:

 const Int32 aConstInt = 4; Object o2 = test ? aShort : aConstInt; // does compile 

The compiler can process the aConstInt link in the second line, as if you just placed 4 there. Based on the context, it turns 4 into short , not Int32 . The compiler behaves differently if it only knows that the input is Int32 , as in the case when it is not const .

If you stated:

 const Int32 aConstInt = short.MaxValue + 1; 

Then the compiler will not allow compilation of the same line:

 Object o2 = test ? aShort : aConstInt; // does not compile 

Because now he considers it as 32768 , which is not short .

+1
source

The reason your first conditional code does not compile is not due to const ; the problem is with data types. aShort is Nullable<Int16> , if you declared it as regular Int16 , it will work just fine

 Int16 aShort = 5; // not nullable Int32 anInt = 5; Object o1 = test ? aShort : anInt; // does compile 

When do you have ? (short?) : (int) ? (short?) : (int) , it cannot perform the conversion because the compiler does not know how to convert (short?) to int .

When do you have ? (short) : (int) ? (short) : (int) , the runtime converts short to int to fit the larger data type.

However, the second condition is compiled thanks to some special magic that the C # compiler uses when processing const integers. When do you have ? (short?) : (const int = 4) ? (short?) : (const int = 4) , the compiler treats const int as short because you declared it as a value that will fit into the short data type. short then implicitly converted to short? as a result.

If you have ? (short) : (const int = 4) ? (short) : (const int = 4) , it behaves exactly the same as in the first case, increasing the conversion of short to int at run time.

If you have ? (short?) : (short?) ? (short?) : (short?) works because the compiler knows how to implicitly convert short to short? .

If you look at the types of results at runtime, you can see it.

 Int16 aShort1 = 5; // not nullable Int16? aShort2 = 5; // nullable object o1 = test ? aShort1 : anInt; object o2 = test ? aShort2 : aConstInt; object o3 = test ? aShort1 : aConstInt; object o4 = test ? aShort1 : aShort2; o1.GetType() // System.Int32 o2.GetType() // System.Int16 o3.GetType() // System.Int32 o4.GetType() // System.Int16 

In fact, if you look at the generated IL for o2 , you will see the following:

 IL_001E: ldarg.0 IL_001F: ldfld UserQuery.test IL_0024: brtrue.s IL_002E IL_0026: ldc.i4.4 // your integer constant IL_0027: newobj System.Nullable<System.Int16>..ctor // Notice the type here IL_002C: br.s IL_0034 IL_002E: ldarg.0 IL_002F: ldfld UserQuery.aShort2 IL_0034: nop IL_0035: box System.Nullable<System.Int16> IL_003A: stloc.1 // o2 
0
source

The C # compiler processes constants specially. This special property extends even to expressions of type 1+2 . null and lambdas are also special: they do not have a CLR type, but you can convert them to any reference type or to any corresponding delegate type. ( null not an object type!).

This is specified by the C # specification. It forces implementations to allow constant expressions and maintain their convertibility properties.

Example:

 short x = 1; //assigning an integer literal to a short - works because it is a constant short y = 1+2; //also works for expressions 

You can also assign an int length without a cast:

 const Int64 a = 1; Int32 b = a; 

Constants just propagate.

0
source

All Articles