3

重複の可能性:
ジェネリック メソッドのオーバーロードに関する問題

簡単なコードを次に示します。

static class Example  
{  
    static int DoIt(object o) { return 0; }    
    class A { }
    static int DoIt(A a) { return 1; }
    static int CallDoIt<X>(X x) { return DoIt(x); }
    static void Main()
    {
        var a = new A();
        System.Console.WriteLine(DoIt(a));      // returns 1 (as desired)
        System.Console.WriteLine(CallDoIt(a));  // returns 0
    }
}

結果は非常に奇妙に見えます: 直接呼び出された関数 DoIt() は、別の関数から呼び出された場合とは異なる値を返します。C# で期待される動作ですか? はいの場合、目的の動作を実現する方法 (できればリフレクションなし)?

4

6 に答える 6

2

これは予想される動作であり、どの型であるかの知識は関数Xに拡張されていませんCallDoItDoIt呼び出される fromのオーバーロードはCallDoIt、引数の型に基づいて静的に決定されますx。は何でもよいのでX、最良の (そして唯一の) 候補はDoIt(object)です。

DoItdynamic を使用して実行時までディスパッチを遅らせることで、この動作を回避できます。

static int CallDoIt<X>(X x) { return DoIt((dynamic)x); }

もう 1 つの方法は、より具体的なバージョンの を提供することですCallDoIt

static int CallDoIt(A a) { return DoIt(a); }
于 2013-01-27T20:32:15.947 に答える
1

DoIt(x)メソッド内のメソッド呼び出しCallDoIt<X>(X x)はどのX型でも機能する必要があるため、C# コンパイラはDoIt(object o)オーバーロードを使用することを選択しました。X次のような型から派生するように型を制限する場合A:

static int CallDoIt<X>(X x) where X : A { return DoIt(x); }

次に、C# コンパイラは、 type が常に type から派生するためDoIt(A a)、オーバーロードを選択できることを認識します。これは、目的の動作を実現する方法に関する 2 番目の質問にも答えます。XA

于 2013-01-27T20:54:53.097 に答える
0

呼び出し方法の選択の決定は、コンパイル時に解決されます。C# コンパイラは、型 X が何であるかを認識せず、DoIt(object o) を使用するメソッドを選択しました。

于 2013-01-27T20:40:49.513 に答える
0

はい、それは予想される動作です。ジェネリック メソッドは、考えられるすべての引数に対して 1 回コンパイルされます。その 1 回のコンパイル中に、DoIt(x)は に解決できないDoIt(A)ため、 でありDoIt(object)、それが常にです。

オブジェクトの種類を動的に確認できます。さらに良いのは、.NET Framework で確認することです。

static int CallDoIt(object x) { return DoIt((dynamic)x); }

ここではCallDoItジェネリックにするメリットがないことに注意してください。これは、このバージョンとまったく同じことを行います。つまり、 ではなくCallDoIt((object)a)を呼び出します。DoIt(A) DoIt(object)

于 2013-01-27T20:33:57.483 に答える
0

CallDoIt に別のオーバーロードを追加します。

static int CallDoIt(A x) { return DoIt(x); }

これにより、物事が機能します。

于 2013-01-27T20:34:50.657 に答える
0

オーバーロードの解決はコンパイル時に発生し (動的な解決が必要ない場合)、X(型パラメーター) の最も具体的な型は ですobject

于 2013-01-27T20:36:57.553 に答える