条件运算符不能隐式转换?

Posted

技术标签:

【中文标题】条件运算符不能隐式转换?【英文标题】:Conditional operator cannot cast implicitly? 【发布时间】:2011-01-14 00:25:07 【问题描述】:

我有点被这个 C# 的小怪癖难住了:

给定变量:

Boolean aBoolValue;
Byte aByteValue;

以下编译:

if (aBoolValue) 
    aByteValue = 1; 
else 
    aByteValue = 0;

但这不会:

aByteValue = aBoolValue ? 1 : 0;

错误提示:“无法将类型 'int' 隐式转换为 'byte'。”

当然,这个怪物会编译:

aByteValue = aBoolValue ? (byte)1 : (byte)0;

这是怎么回事?

编辑:

使用 VS2008,C# 3.5

【问题讨论】:

Nullable types and the ternary operator: why is `? 10 : null` forbidden?的可能重复 【参考方案1】:

这是一个相当常见的问题。

在 C# 中,我们几乎总是从内到外进行推理。当你看到

x = y;

我们计算出 x 的类型是什么,y 的类型是什么,以及 y 的类型是否与 x 的赋值兼容。但是当我们计算 y 的类型时,我们并没有使用我们知道 x 的类型这一事实。

那是因为 x 可能不止一个:

void M(int x)  
void M(string x)  
...
M(y); // y is assigned to either int x or string x depending on the type of y

我们需要能够计算出表达式的类型不知道它被分配到什么。类型信息从表达式流出,而不是流入表达式。

为了计算条件表达式的类型,我们计算结果和替代表达式的类型,选择这两种类型中更一般的类型,这成为条件表达式的类型。所以在你的例子中,条件表达式的类型是“int”,它不是一个常量(除非条件表达式是常量真或常量假)。由于它不是常量,因此不能将其分配给字节;当结果不是常量时,编译器仅根据类型而非值进行推理。

所有这些规则的例外是 lambda 表达式,其中类型信息确实从上下文流入 lambda。使逻辑正确是非常困难的。

【讨论】:

我的心都碎了。谢谢你,先生,这一部分是启发性的,两部分是令人沮丧的。一是因为它不能像看起来那样工作,二是因为它不能那样工作是有道理的。所以......它是常量! 谢谢。我一直在您的博客上寻找有关此主题的帖子,但这更好。 @John:我在这里稍微谈谈这些问题:blogs.msdn.com/ericlippert/archive/2006/05/24/… @Eric Lippert,我试图对您的博客条目发表评论,但我不确定它是否需要,所以我会在这里问:与其尝试独立解析类型,为什么不编译器只是将整个三元表达式转换为等效的“详细表达式”(在本例中为byte aByteValue; if (aBoolValue) aByteValue = 1; else aByteValue = 0;)?是否有一些问题会使这不可行?或者它是哲学的东西(例如,“如果它看起来像一个表达式,它应该被评估为一个。”)?我认为大多数程序员最初会期望三元组的工作方式与 if-else 完全一样。 var x = yint x = y 不同,应区别对待。在第一种情况下,我要求编译器解决这个问题,在后一种情况下,我告诉它我的期望。而且,正如@devuxer 所说,大多数开发人员都希望?: 能够像if-else 一样工作。【参考方案2】:

我正在使用 VS 2005,对于 bool 和 Boolean,我可以重现,但不是 true

 bool abool = true;
 Boolean aboolean = true;
 Byte by1 = (abool ? 1 : 2);    //Cannot implicitly convert type 'int' to 'byte'
 Byte by2 = (aboolean ? 1 : 2); //Cannot implicitly convert type 'int' to 'byte'
 Byte by3 = (true ? 1 : 2);     //Warning: unreachable code ;)

最简单的解决方法似乎是这个演员表

 Byte by1 = (Byte)(aboolean ? 1 : 2);

所以,是的,似乎三元运算符导致常量将它们的类型“固定”为整数并禁用隐式类型转换,否则您会从适合较小类型的常量中获得。

【讨论】:

你的解释很有道理。但是为什么常数在这种情况下不能固定呢?并对门迪的发现倍加好奇。 Microsoft 的某个人应该知道,并且可能需要与之强烈争论...... 我认为 Mendy 发现,当编译器可以轻松检测到它可以完全优化三元运算符时,它的编译代码等于 Byte by = 2,它保留了隐式转换的能力。 (请参阅我对上面编译器警告的评论) 我认为 Eric Lippert 可能已经在他的博客上讨论过为什么会这样。 @John Knoeller:你能找到链接吗? @Mendy:找到了blogs.msdn.com/ericlippert/archive/2006/05/24/…【参考方案3】:

我可能没有很好的答案给你,但如果你在很多地方这样做,你可以声明:

private static readonly Byte valueZero = (byte)0;
private static readonly Byte valueOne = (byte)1;

只有这些变量。如果 const 在项目本地,您可能会侥幸逃脱。

编辑: 使用 readonly 没有意义 - 这些永远不会改变。

【讨论】:

我确定这只是一个错字,但您两次声明了同一个变量。 @Hamish:我明白你的意思了。我可以使用 const,但这是一种解决方法。不过,这当然不值得你投反对票。

以上是关于条件运算符不能隐式转换?的主要内容,如果未能解决你的问题,请参考以下文章

Solidity类型转换

条件与运算符

何时会发生隐式类型转换

为啥 Linq Cast<> 助手不能与隐式转换运算符一起使用?

条件运算符赋值[重复]

JS 的5个不良编码习惯