24

同じメソッドのデリゲートを暗黙的に作成できるのに、ジェネリック関数にパラメーターとして渡されたときに、C# 3.0 コンパイラーがメソッドの型を推測できないのはなぜでしょうか。

次に例を示します。

class Test
{
    static void foo(int x) { }
    static void bar<T>(Action<T> f) { }

    static void test()
    {
        Action<int> f = foo; // I can do this
        bar(f); // and then do this
        bar(foo); // but this does not work
    }   
}

fooに渡してbar、渡される関数のシグネチャからコンパイラに型を推測させることができると思っていAction<T>ましたが、これは機能しません。ただし、キャストせずにAction<int>fromを作成できるfooので、コンパイラが型推論を介して同じことを行うことができないという正当な理由はありますか?

4

5 に答える 5

18

多分これはそれをより明確にするでしょう:

public class SomeClass
{
    static void foo(int x) { }
    static void foo(string s) { }
    static void bar<T>(Action<T> f){}
    static void barz(Action<int> f) { }
    static void test()
    {
        Action<int> f = foo;
        bar(f);
        barz(foo);
        bar(foo);
        //these help the compiler to know which types to use
        bar<int>(foo);
        bar( (int i) => foo(i));
    }
}

fooはアクションではありません-fooはメソッドグループです。

  • 代入ステートメントでは、int型が指定されているため、コンパイラはあなたが話しているfooを明確に知ることができます。
  • barz(foo)ステートメントでは、int型が指定されているため、コンパイラーは、話しているfooを判別できます。
  • bar(foo)ステートメントでは、単一のパラメーターを持つ任意のfooである可能性があるため、コンパイラーはあきらめます。

編集:コンパイラが型を理解するのに役立つ2つの(さらに)方法を追加しました(つまり、推論手順をスキップする方法)。

JSkeetの回答の記事を読んだところ、タイプを推測しないという決定は、次のような相互推測のシナリオに基づいているようです。

  static void foo<T>(T x) { }
  static void bar<T>(Action<T> f) { }
  static void test()
  {
    bar(foo); //wut's T?
  }

一般的な問題は解決できないため、解決策が存在する特定の問題を未解決のままにすることを選択します。

この決定の結果として、メソッドにオーバーロードを追加したり、単一のメンバーメソッドグループに使用されているすべての呼び出し元から多くの型の混乱を招いたりすることはありません。それは良いことだと思います。

于 2009-01-02T21:00:10.773 に答える
7

その理由は、タイプが拡張されたとしても、失敗する可能性はないはずだからです。つまり、メソッドfoo(string)が型に追加された場合、既存のメソッドの内容が変更されない限り、既存のコードには関係ありません。

そのため、メソッドfooが1つしかない場合でも、fooへの参照(メソッドグループと呼ばれる)を、などの非タイプ固有のデリゲートにキャストすることはできませんが、などのタイプ固有のデリゲートにAction<T>のみキャストできAction<int>ます。

于 2009-01-02T21:05:37.033 に答える
5

はい、それは少し奇妙です。型推論のC#3.0仕様は読みにくく、間違いがありますが、機能するはずです。最初のフェーズ(セクション7.4.2.1)では、間違いがあると思います-最初の箇条書きでメソッドグループについて言及するべきではありません(明示的なパラメーター型推論(7.4.2.7)でカバーされていないため)-つまり、出力型推論(7.4.2.6)。それ機能するはずですが、明らかに機能しません:(

MSが型推論の仕様を改善しようとしていることは知っているので、もう少し明確になるかもしれません。また、読みにくいにもかかわらず、メソッドグループと型推論には制限があることも知っています。確かに、メソッドグループが実際には単一のメソッドである場合に特別な場合があります。

Eric Lippertには、この場合と同様に、メソッドグループで機能しないリターン型推論に関するブログエントリがありますが、ここでは、パラメータタイプのみについて、リターンタイプには関心がありません。しかし、彼の型推論シリーズの他の投稿が役立つ可能性があります。

于 2009-01-02T21:02:55.277 に答える
5

割り当てに注意してください

Action<int> f = foo;

すでにたくさんのシンタックスシュガーがあります。コンパイラは実際にこのステートメントのコードを生成します。

Action<int> f = new Action<int>(foo);

対応するメソッド呼び出しは問題なくコンパイルされます。

bar(new Action<int>(foo));

Fwiw、コンパイラが型引数を推測するのを助けます:

bar<int>(foo);

要するに、代入ステートメントには砂糖が含まれているのに、メソッド呼び出しには含まれていないのはなぜでしょうか? 割り当てで砂糖が明確であるため、可能な置換は1つしかないためだと推測する必要があります。しかし、メソッド呼び出しの場合、コンパイラの作成者はオーバーロード解決の問題に既に対処しなければなりませんでした。そのルールは非常に精巧です。彼らはおそらくそれに慣れていませんでした。

于 2009-01-02T22:27:33.903 に答える
0

完全を期すために、これは C# に固有のものではありません。同じ VB.NET コードが同様に失敗します。

Imports System

Module Test
  Sub foo(ByVal x As integer)
  End Sub
  Sub bar(Of T)(ByVal f As Action(Of T))
  End Sub

  Sub Main()
    Dim f As Action(Of integer) = AddressOf foo ' I can do this
    bar(f) ' and then do this
    bar(AddressOf foo) ' but this does not work
  End Sub
End Module

エラー BC32050: 'Public Sub bar(Of T)(f As System.Action(Of T))' の型パラメーター 'T' を推論できません。

于 2011-07-04T14:36:47.910 に答える