22

理解できない型推論エラーに出くわしたとき、趣味のプロジェクトで遊んでいました。私はそれを次の簡単な例に単純化しました。

私は次のクラスと関数を持っています:

class Foo { }
class Bar { }
class Baz { }

static T2 F<T1, T2>(Func<T1, T2> f) { return default(T2); }
static T3 G<T1, T2, T3>(Func<T1, Func<T2, T3>> f) { return default(T3); }

次に、次の例を検討してください。

// 1. F with explicit type arguments - Fine
F<Foo, Bar>(x => new Bar());

// 2. F with implicit type arguments - Also fine, compiler infers <Foo, Bar>
F((Foo x) => new Bar());

// 3. G with explicit type arguments - Still fine...
G<Foo, Bar, Baz>(x => y => new Baz());

// 4. G with implicit type arguments - Bang!
// Compiler error: Type arguments cannot be inferred from usage
G((Foo x) => (Bar y) => new Baz());

最後の例ではコンパイラエラーが発生しますが、問題なく型引数を推測できるはずです。

質問:<Foo, Bar, Baz>この場合、コンパイラが推測できないのはなぜですか?

更新: 2番目のラムダを恒等関数でラップするだけで、コンパイラーがすべての型を正しく推測することを発見しました。

static Func<T1, T2> I<T1, T2>(Func<T1, T2> f) { return f; }

// Infers G<Foo, Bar, Baz> and I<Bar, Baz>
G((Foo x) => I((Bar y) => new Baz()));

なぜそれはすべての個々のステップを完全に行うことができますが、一度にすべての推論を行うことはできませんか?コンパイラが暗黙のラムダ型と暗黙のジェネリック型を分析する順序に微妙な点はありますか?

4

3 に答える 3

19
于 2012-09-17T14:57:06.067 に答える
1

ラムダは、割り当てられておらず、コンパイラによって決定できないため、戻り値の型が何であるかを推測できません。ラムダの戻り値の型がコンパイラによってどのように決定されるかについては、このリンクを確認してください。あなたが持っていた場合:

Func<Bar, Baz> f = (Bar y) => new Baz();
G((Foo x) => f);

コンパイラは、割り当てられているものに基づいてラムダの戻り値の型を計算できたはずですが、現在は何にも割り当てられていないため、コンパイラは戻り値の型を判断するのに苦労してい(Bar y) => new Baz();ます。

于 2012-09-04T06:07:02.287 に答える
0

コンパイラにとって、ラムダ関数は Func とは異なります。つまり、Func にラムダ関数を使用すると、型変換が行われます。ジェネリックを特殊化する場合、コンパイラは「ネストされた」型変換を行いません。ただし、あなたの例ではそれが必要になります:

の型は(Foo x) => (Bar y) => new Baz ()ですがlambda (Foo, lambda (Bar, Baz))Func (T1, Func (T2, T3))ネストされている 2 つの変換が必要です。

于 2012-09-04T07:46:37.670 に答える