3

可能重复:
条件运算符不能隐式转换?

我遇到了一个特殊的情况,想知道为什么我必须这样做。我正在使用.NET 3.5。

这有效:

short foo;

if (isValid)
    foo = -1;
else
    foo = getFoo();

这不起作用:

short foo;
foo = isValid ? -1 : getFoo();

我必须对 -1 进行类型转换:

short foo;
foo = isValid ? (short)-1 : getFoo();

三元表达式有什么不同?它认为 -1 是一个需要转换为 short 的 int。但为什么?

4

4 回答 4

11

一些东西。

首先,条件运算符是三元运算符,而不是三元运算符

其次,我注意到在您的代码示例中,两个旨在等效的代码示例不是:

short foo;
if (isValid)
    foo = -1;
else
   getFoo();

不一样

short foo = isValid ? (short)-1 : getFoo();

如果 isValid 为假,则前者使 foo 未分配。后者分配 foo 而不管 isValid 的值。

我假设你的意思是

short foo;
if (isValid)
    foo = -1;
else
    foo = getFoo();

而且, getFoo() 返回短。

问题是为什么没有类型转换的条件运算符中的转换是非法的,但 if 语句的结果是合法的。

它在 if 语句中是合法的,因为规范的第 6.1.9 节规定:

如果常量表达式的值在目标类型的范围内,则 int 类型的常量表达式可以转换为 sbyte、byte、short、ushort、uint 或 ulong 类型。

-1 是 int 类型的常量表达式,在 short 范围内,因此可以隐式转换为 short。

那么为什么条件表达式形式是伪造的呢?

我们首先要明确的是,条件表达式的类型是由它的内容决定的,而不是它的上下文。赋值右侧的表达式类型不取决于它被赋值给什么!假设你有

short M(short x){...}
int M(int x){...}

short y = M(-1);

我认为您不会期望重载解决方案会说“好吧,我通常会选择 M(int),因为 -1 是一个 int,但不,我会选择 M(short),否则分配将不会”不行。” 重载决议对结果的去向一无所知。它的工作是根据给定的参数确定正确的重载,而不是基于调用的上下文。

确定条件表达式的类型的工作方式相同。我们不看它的类型,我们看表达式中的类型。

好的,所以我们已经确定 this 被分配给 short 的事实与确定表达式的类型无关。但这仍然留下了一个问题“为什么条件表达式的类型是 int 而不是 short?”

这是一个很好的问题。让我们看看规范。

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

如果具有 X 类型并且 y 具有 Y 类型,则:

如果存在从 X 到 Y 的隐式转换,但不存在从 Y 到 X 的转换,则 Y 是条件表达式的类型。

如果存在从 Y 到 X 的隐式转换,但不存在从 X 到 Y 的转换,则 X 是条件表达式的类型。

否则,无法确定表达式类型,并出现编译时错误。

在这种情况下,操作数都有一个类型。(那里关于“如果 x 有类型...”的措辞是针对其中有 null 或 lambda 的情况;那些没有类型!)第一个操作数是 int 类型,第二个是键入简短。

存在从 short 到 int 的隐式转换,但不存在从 int 到 short 的转换。因此条件表达式的类型是int,不能赋值为short。

现在,可以说这个算法没有它想象的那么好。我们可以使算法大大复杂化以处理存在两种可能的“候选”类型的所有情况——在这种情况下,int 和 short 都是合理的候选者,因为当被视为特定表达式时,两个分支都可以转换为 int 和 short,而不仅仅是拥有类型。在这种情况下,我们可以说这两种类型中较小的一种是首选类型。

(有时在 C# 中,我们说两种类型中越通用越好,但在这种情况下,您会希望我们选择更具体的类型。不幸的是,语言在这个特定的设计方面并不一致;我个人宁愿我们总是选择更具体的,但在某些类型推断场景中,现在这将是一个重大变化。)

早在 2006 年我就考虑过这样做。在设计 LINQ 如何处理有多种类型可供选择并且必须选择一种作为“最佳”的情况时,我们注意到条件运算符已经必须解决这个问题,并且此外,在 C# 2 中,它实际上并没有按照规范实现。对此进行了长时间的辩论,我们最终对条件运算符的规范进行了一些小的修改,以使其更符合其实现的(和期望的)行为。然而,当有多种类型可供选择时,我们决定不采取较大的突破性变化来调整算法以使用两种可能类型中较小的一种。

对于这个问题的一些思考,请参阅我 2006 年的帖子:

于 2010-06-30T17:03:50.173 回答
6

因为 -1 默认是一个整数。编译器告诉你必须明确告诉它要做什么比编译器假设你想要做什么要安全得多。

你的例子很简单。通过一些额外的工作,编译器显然可以看到您希望 -1 是一个短的。尽管隐式转换并不那么简单,但所有边缘情况都伴随着隐式转换。如果你为编译器添加一个规则来假设你想要什么,你必须将它应用于每一种情况,而不仅仅是一种情况,这就是它变得困难的地方。

作为注释,您应该查看Eric Lippert 的博客,因为我知道他解释了为什么编译器不做出这样的假设。

于 2010-06-30T14:17:10.653 回答
1

应用于 anint和 a的条件运算符short的类型为int; 编译器不会从您分配给它的类型推断表达式的类型。

您不能将此short表达式隐式转换为int.

于 2010-06-30T14:17:41.697 回答
0

条件运算符强制两个可能的结果表达式属于同一类型。在这种情况下,左边是一个int,右边是一个方法short返回的getPoo。由于将 short 转换为 an 总是安全的,int因此编译器选择操作的结果为int.

所以结果将是 a 分配int给 a short,这就是为什么你需要明确地将它转换为 short 的原因。

如果您明确使用 if/else 方法,您将把一个文字整数分配给一个短整数,这允许编译器验证文字整数是否安全地分配给一个短整数,而无需显式强制转换。

有关内部解释,请查看:

演员不遵守分配律

于 2010-06-30T14:22:47.087 回答