4

当我将更窄的可空值和更宽的数值组合为条件运算符的第二个和第三个操作数时,我得到了我在 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

4 回答 4

4

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

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

If x has type X and y has type Y then

  • If an implicit conversion (§6.1) exists from X to Y, but not from Y to X, then Y is the type of the conditional expression.
  • If an implicit conversion (§6.1) exists from Y to X, but not from X to Y, then X is the type of the conditional expression.
  • Otherwise, no expression type can be determined, and a compile-time error occurs.

And §6.1.9 (Implicit constant expression conversions) states the following:

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.

As you can see, constant expressions of type int and long are treated specially.

Thus the expression test ? aShort : aConstInt is valid because there is an implicit conversion from the int constant expression 4 to short and then to short? (and thus the type of the expression is short?), but test ? aShort : anInt is not valid because there is neither an implicit conversion from a non-constant expression of type int to short? nor from short? to int.

于 2013-04-07T22:30:02.987 回答
1

当你写:

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

编译器能够将aConstInt第二行中的引用视为您只是将 a 放在4那里。根据上下文,它将 the4变成 a short,而不是 a Int32。如果编译器只知道输入是 a ,则编译器的行为会有所不同Int32,就像它不是 a 的情况一样const

如果您声明:

const Int32 aConstInt = short.MaxValue + 1;

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

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

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

于 2013-04-07T21:39:56.740 回答
0

您的第一个条件无法编译的原因不是因为const; 问题在于数据类型。aShort是 a Nullable<Int16>,如果您将其声明为正常的Int16,它会正常工作

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

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

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

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

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

如果您有? (short?) : (short?)作品,因为编译器知道如何将 a 隐式转换short为 a 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

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

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
于 2013-04-07T21:35:35.387 回答
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;

常数只是传播。

于 2013-04-07T21:39:38.830 回答