2013-12-18 4 views
9

私は、次の推論のテストを行った:C#はジェネリック型の引数型を非ジェネリックな静的なメソッドのシグネチャから推測できないのはなぜですか?

static class InferenceTest { 
    static void TakeInt(int a) { } 
    static int GiveInt() { return 0; } 
    static int TakeAndGiveInt(int a) { return 0; } 

    static void ConsumeAction1<T>(Action<T> a) { } 
    static void ConsumeFunc1<T>(Func<T> f) { } 
    static void ConsumeFunc2a<T1, T2>(Func<T1, T2> f) { } 
    static void ConsumeFunc2b<T>(Func<int, T> f) { } 
    static void ConsumeFunc2c<T>(Func<T, T> f) { } 
    static void ConsumeFunc1Func2<T1, T2>(Func<T1> f1, Func<T1, T2> f2) { } 

    static void Main() { 
     ConsumeAction1(TakeInt);  //error 
     ConsumeFunc1(GiveInt);   //ok 
     ConsumeFunc2a(TakeAndGiveInt); //error 
     ConsumeFunc2b(TakeAndGiveInt); //ok 
     ConsumeFunc2c(TakeAndGiveInt); //error 
     ConsumeFunc1Func2(GiveInt, TakeAndGiveInt); //ok 
    } 
} 

結果は、C#コンパイラは、非ジェネリックメソッドグループからデリゲート関数のパラメータのためのジェネリック型引数を推論することができないことを示唆しているように見えます。最も私を困惑何

は、C#はConsumeFunc1Func2にメソッドの戻り値からFunc<T1, T2>ための型引数を推論することができているということですが、ConsumeFunc2cFunc<T, T>のための型を推論することができません。

この質問はT of Func<S, T> is inferred from output of lambda expression only when S and T are different?の質問に似ていますが、パラメータタイプが不明なラムダの代わりに非ジェネリックメソッドグループがあります。

Why can't C# infer type from this seemingly simple, obvious case質問ソートの回答質問「非あいまいではない非ジェネリックメソッドが推論には不十分なのはなぜですか?」 "なぜ引数型と推論の戻り値型が異なるのですか?"

質問:

なぜC#コンパイラは、戻り値の型を使用してFunc<T>の種類を推測することができますが、Func<T, T>場合の成功を見ることができませんか?

なぜC#コンパイラはConsumeFunc1Func2Func<T1>からFunc<T1, T2>ためT1型引数を推論することができますが、より簡単であるように思われConsumeFunc2c自体からFunc<T, T>ためT型引数を推論することはできませんか?

+0

ConsumeFunc1Func2では、コンパイルは依然としてパラメータ型ではなく戻り値からのみ推測されます。 T1はGiveIntの戻り値から解決され、T2はTakeAndGiveIntの戻り値から解決されます。したがって、ConsumeFunc1Func2のケースで追加された謎はありません。 – Baldrick

+1

私はC#4.0仕様のセクション7.5.2をよく読んでいます。これは非常に読みやすく、型推論のさまざまな段階、およびそれらがメソッドグループにどのように関係しているかを記述します。 – Baldrick

+0

'ConsumeFunc2b'は' Func 'の戻り値の型' T'を 'TakeAndGiveInt'から解決できることを示しています。しかし、 '?'も 'ConsumeFunc2c'の' Func 'の場合のように' T'であるとき、コンパイラはパラメータ 'T'を既に忘れていると思われます。 'ConsumeFunc1Func2'の成功とはまったく異なります。 –

答えて

1

メソッドパラメータは検査されません。

説明したように、ConsumeFunc1Func2では、コンパイラは戻り値からのみ推論します。 ConsumeFunc2cでは、TakeAndGiveIntシグネチャが検査されず、そのメソッドのパラメータ型が実際に同じ型のメソッドの戻り値の型の原因であるかどうかを確認しません。メソッドのパラメータは検査されません。

1

一般に、メソッド名は、メソッドグループが割り当てられるユニークなタイプAction<T>を一意に識別しません。例えば、Fredのオーバーロードが1つしかなく、単一のCat引数をとっても、そのオーバーロードはAction<Cat>だけでなく、Action<Mammal>,Action<Animal>、またはAction<Object>のような他のタイプに割り当てることができます。 1つのタイプ置換があらゆる方法よりも優れている場合もありますが、必ずしもそうであるとは限りません。コンパイラが推測すると、変更を壊すべきではない多くのことが意味されるので、コンパイラが "推測"しようとするよりも、デリゲートの型を指定することを要求する言語を定義する方がクリーンです(例えばメソッドのオーバーロードを追加すると、以前は動作していた型推論があいまいになることがあります)。

関連する問題