【问题标题】:Why do I have to typecast an int in a ternary expression? [duplicate]为什么我必须在三元表达式中对 int 进行类型转换? [复制]
【发布时间】:2011-03-10 04:04:06
【问题描述】:

可能重复:
Conditional operator cannot cast implicitly?

我遇到了一个特殊的情况,想知道为什么我必须这样做。我正在使用.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。但为什么呢?

【问题讨论】:

  • 你可以选择更好的变量和函数名...
  • getFoo的返回类型是什么?
  • @Oded getFoo 的类型很短。这不是问题
  • 最好使用常量来表示“-1”而不是“魔法值”,因为这通常会使您的代码更易于阅读。
  • 这不是重复的。他在问为什么编译器不将文字 -1 转换为short。他的错误不在条件范围内。

标签: c# ternary-operator casting boolean-expression


【解决方案1】:

一些事情。

首先,条件运算符是ternary operator, not a tertiary operator

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

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

不一样

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

如果 isValid 为 false,则前者使 foo 未赋值。后者不管 isValid 的值如何都分配 foo。

我猜你的意思是

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

此外,getFoo() 返回short。

问题是为什么没有类型转换的条件运算符中的转换是非法的,但 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 年的帖子:

【讨论】:

  • 感谢您的洞察力。你是对的,我的意思是 foo = getFoo();
【解决方案2】:

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

所以结果是将int 分配给short,这就是为什么您需要将其显式转换为short。

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

内部解释请看:

Cast operators do not obey the distributive law

【讨论】:

    【解决方案3】:

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

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

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

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-03-19
        • 2018-07-13
        • 2010-09-24
        • 1970-01-01
        • 2021-06-16
        • 2020-01-08
        • 1970-01-01
        相关资源
        最近更新 更多