条件运算符混合包装类型和常量整数表达式

时间:2013-04-07 21:29:01

标签: c# conditional-operator

当我将条件运算符的第二个和第三个操作数组合成一个更窄的可空和更宽的数值时,我得到了我在C#中没有想到的东西。这不起作用,但我发现如果更宽的数值是int类型的常量表达式而更窄的可空类型是SByte类型呢?还是Int16?演示:

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

我的问题是,如果我的int是常量,为什么会编译?我在C#语言规范中找不到对此的引用,这里发生了什么?

4 个答案:

答案 0 :(得分:4)

在C#4.0语言规范中,§7.14(条件运算符)声明如下:

  

x运算符的第二个和第三个操作数y?:控制条件表达式的类型。

     

如果x的类型为Xy的类型为Y,那么

     
      
  • 如果从XY存在隐式转换(第6.1节),而不是YX,则Y是条件表达式。
  •   
  • 如果从YX存在隐式转换(第6.1节),而不是XY,则X是条件表达式。
  •   
  • 否则,无法确定表达式类型,并发生编译时错误。
  •   

并且§6.1.9(隐式常量表达式转换)声明如下:

  

隐式常量表达式转换允许以下转换:

     
      
  • 类型int常量表达式(第7.19节)可以转换为sbytebyteshort,{{ 1}},ushortuint,前提是常量表达式的值在目标类型的范围内。
  •   
  • 类型ulong常量表达式可以转换为long类型,前提是常量表达式的值不是负数
  •   

如您所见,ulongint类型的常量表达式是专门处理的。

因此,表达式long是有效的,因为存在从test ? aShort : aConstInt常量表达式int4然后再到short的隐式转换(因此表达式的类型为short?),但short?无效,因为既没有从test ? aShort : anInt类型的非常量表达式到int的隐式转换,也没有short?short?

答案 1 :(得分:1)

当你写:

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

编译器能够处理第二行中的aConstInt引用,就像您只是在其中放置4一样。根据上下文,它会将4变为short,而不是Int32。如果编译器仅知道输入是Int32,则编译器的行为会有所不同,就像它不是const时一样。

如果您已声明:

const Int32 aConstInt = short.MaxValue + 1;

然后编译器不允许编译同一行:

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

因为它现在将其视为32768,而不是short

答案 2 :(得分:0)

你的第一个条件不能编译的原因不是因为const;问题在于数据类型。 aShortNullable<Int16>,如果您将其声明为正常Int16,它就可以正常使用

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

当你有? (short?) : (int)时,它无法进行转换,因为编译器不知道如何将(short?)转换为int

当您拥有? (short) : (int)时,运行时正在将short转换为int以适应更大的数据类型。

然而,第二个条件进行编译,这要归功于C#编译器在处理const整数时使用的一些特殊魔法。当您拥有? (short?) : (const int = 4)时,编译器会将const int视为short,因为您已将其声明为适合short数据类型的值。然后short会在结果中隐式转换为short?

如果你有? (short) : (const int = 4),它的行为就像第一种情况一样,在运行时将short上转换为int

如果? (short?) : (short?)有效,因为编译器知道如何将short隐式转换为short?

如果你在运行时查看结果类型,你实际上可以看到这一点。

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

事实上,如果您查看o2生成的IL,您会看到:

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

答案 3 :(得分:0)

C#编译器专门处理常量。此特殊属性甚至在1+2等表达式中传播。 null和lambdas也很特殊:它们没有CLR类型,但您可以将它们转换为任何引用类型或任何匹配的委托类型。 (null 类型为对象!)。

这是C#规范的强制要求。它强制实现解析常量表达式并维护其可转换性属性。

示例:

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

您还可以在没有强制转换的情况下将long指定给int:

const Int64 a = 1;
Int32 b = a;

常量只是传播。