一些事情。
首先,条件运算符是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 年的帖子: