ここに投稿された正解に少し追加すると、この仕様につながる設計ガイドラインが 2 つあります。
1つ目は、「内から外へ」と推論することです。あなたが言う時
double x = 2 + y;
最初に x の型、次に 2 の型、次に y の型、次に (2+y) の型を計算し、最後に x と (2+y) に互換性のある型があるかどうかを計算します。ただし、2、y、または 2+y の型を決定する際に、x の型は使用しません。
これが良いルールである理由は、多くの場合、「レシーバー」のタイプがまさに私たちが解決しようとしているものだからです。
void M(Foo f) {}
void M(Bar b) {}
...
M(x ? y : z);
ここで何をすべきですか?これが Foo に行くのか Bar に行くのかを決定するために、オーバーロードの解決を行うために、条件式のタイプを解決する必要があります。したがって、これがたとえば Foo に行くという事実を、条件式の型の分析に使用することはできません! それはニワトリが先か卵が先かの問題です。
このルールの例外はラムダ式で、コンテキストから型を取ります。その機能を適切に動作させることは非常に複雑でした。興味があれば、ラムダ式と匿名メソッドに関する私のブログ シリーズを参照してください。
2 番目の要素は、型を「魔法のように」作成することは決してないということです。型を推測しなければならないものがたくさん与えられたとき、私たちは常に実際に目の前にある型を推測します。
あなたの例では、分析は次のようになります。
- 結果のタイプを計算する
- 代替の型を計算する
- 結果と代替の両方に適合する最適な型を見つける
- 条件式の型から、条件式を使用している物の型への変換があることを確認してください。
最初のポイントに沿って、外側から内側への推論は行いません。式の型を計算するために、変数の型を知っているという事実を使用しません。しかし、今興味深いのは、あなたが持っているときです
b ? new Cat() : new Dog()
「条件式の型は集合 {Cat, Dog} の中で最適な型です」と言います。「条件式の型は、Cat と Dog の両方に対応する最適な型」とは言いません。それは哺乳類ですが、私たちはそうしません。代わりに、「結果は実際に見たものでなければならない」と言い、これら2つの選択肢のうち、どちらも明確な勝者ではありません. あなたが言ったなら
b ? (Animal) (new Cat()) : new Dog()
次に、Animal と Dog のどちらかを選択します。Animal が明らかに勝者です。
さて、この型分析を行うとき、実際には C# 仕様を正しく実装していないことに注意してください! エラーの詳細については、私の記事を参照してください。