一些东西。
首先,条件运算符是三元运算符,而不是三元运算符。
其次,我注意到在您的代码示例中,两个旨在等效的代码示例不是:
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 年的帖子: