条件表达式的类型无法确定?

我刚刚遇到这个(编写代码来演示“问题”):

public ICollection CreateCollection(int x) { ICollection collection = x == 0 ? new List() : new LinkedList(); return collection; } 

编译器抱怨:

Fehler CS0173:Der Typ des bedingten Ausdrucks kann nicht bestimmt werden,weil keine implizite Konvertierung zwischen“System.Collections.Generic.List”und“System.Collections.Generic.LinkedList”erfolgt。

其翻译大致为:

无法确定条件运算符的类型,因为List和LinkedList之间没有隐式转换。

我可以看到为什么编译器会抱怨,但是,嘿,来吧。 它试图发挥愚蠢。 我可以看到两个表达式不是同一类型,但有一个共同的祖先,作为奖励,左侧的类型也是共同的祖先。 我相信编译器也可以看到它。 如果左侧被声明为var我可以理解错误。

我在这里想念的是什么?

编辑:

我接受詹姆斯·冈特的解释。 也许只是为了说清楚。 我可以很好地阅读编译器规范。 我想了解原因。 为什么有人决定以这种方式编写规范。 这种设计背后一定有原因。 根据詹姆斯的说法,设计原则是“没有惊喜”。 此外,CodeInChaos还解释了如果编译器试图从常见的祖先中推断出类型,您可能遇到的惊喜。

表达式(a?b:c)必须解析为一种类型。 类型将是b或c的类型。 如果它们不同(并且没有从一个到另一个的隐式转换),则编译器在编译时不知道这是哪种类型。

您可能会说它应该推断出存在共同的根类型,但总有一个共同的根类型(例如Object)。

通常,C#编译器不会尝试猜测你的意思。 如果要使用公共根类型,则将b和c强制转换为该类型。

这种逻辑在整个C#的设计中运行,偶尔会有点烦人,但更常见的是它会阻止你犯错误。

由于接口,它们可以具有多个不同的共同祖先。

可以添加一个要求,即如果祖先是明确的,它只会自动转换。 但是,然后添加类实现的其他接口突然变成了一个突破性变化。 这可能并不可取。

例如,假设您使这些类型实现ISerializeable 。 这不应该改变你的代码的行为,但是如果你支持这种转换到通用接口,它会。

编辑:想一想它,并注意到这个函数已经有完全相同的问题:

 T MyFunc(T left,T right) 

而且这段代码没有编译:

 ICollection r=MyFunc(new List() , new LinkedList()); 

因为它无法决定使用哪种类型作为类型参数T 因此?:运算符的行为与重载决策一致。

在确定右侧的类型时, 根本不考虑左侧。

只有当编译器独立确定右侧的类型时,它才会检查与左侧的分配兼容性。

至于你声称两​​种类型“都有一个共同的祖先” :那是ICollectionIEnumerableICollectionIEnumerable还是Object ? 编译器应该使用哪种启发式方法来明确地确定想要的类型? 编译器只是要求您指定,而不是试图猜测您的意图。

它只是?:的定义要求相同的类型。
你当然可以使用

 ? (ICollection) new List() : (ICollection) new LinkedList(); 

或者只使用if/else

根据C#参考,§14.13,

[Given]formsb ? x : y条件表达式b ? x : y b ? x : y

  • 如果X和Y是相同的类型,那么这是条件表达式的类型。
  • 否则,如果从X到Y存在隐式转换(第13.1节),而不是从Y到X,则Y是条件表达式的类型。
  • 否则,如果从Y到X存在隐式转换(第13.1节),而不是从X到Y,则X是条件表达式的类型。
  • 否则,不能确定表达式类型,并发生编译时错误。

在你的情况下,X和Y都转换为Z,但这没有帮助。 在语言中,一般原则是编译器在应用规则时甚至不查看目标变量。 简单的例子: double a = 7 / 2; // a becomes 3.0 double a = 7 / 2; // a becomes 3.0

所以在阅读完这篇文章之后,只需将其中一个结果转换为ICollection 。 我没有测试过。