6

Expressionパラメーターを使用してプロパティ メンバーを取得し、それに基づいて動作するいくつかの拡張メソッドがあり、メンバーがIEnumerable<>である特定のケースのオーバーロードがあります。ただし、ジェネリック クラス内から呼び出された場合に予想されるメソッド オーバーロードと一致しないようです (以下の r4 の場合)。クラスの外では、正しいメソッドが選択されます。

何が起きてる?これはうまくいくでしょうか、それとも新しいアプローチを見つける必要がありますか?

(これはC#5にあります)

public class Test
{
    public void MyTest()
    {
        // returns "Object"
        var r1 = new MyClass<object>().Ext(a => a.Content);

        // returns "Enumerable"
        var r2 = new MyClass<IEnumerable<object>>().Ext(a => a.Content);

        // returns "Object"
        var r3 = new MyClass<object>().TestExt();

        // returns "Object" (I was expecting "Enumerable")
        var r4 = new MyClass<IEnumerable<object>>().TestExt();

        // returns "Enumerable"
        var r5 = new MyClass<int>().TestExt2();
    }
}

public class MyClass<T>
{
    public T Content { get; set; }

    public IEnumerable<object> OtherContent { get; set; }

    public string TestExt()
    {
        return this.Ext(a => a.Content);
    }

    public string TestExt2()
    {
        return this.Ext(a => a.OtherContent);
    }
}

public static class MyExtensions
{
    public static string Ext<T>(this T obj, Expression<Func<T, IEnumerable<object>>> memberExpression)
    {
        return "Enumerable";
    }

    public static string Ext<T>(this T obj, Expression<Func<T, object>> memberExpression)
    {
        return "Object";
    }
}
4

1 に答える 1

4

ジェネリックは動的型付けではありません。呼び出すオーバーロードは、コンパイル時に凍結されます。プログラムが後で実行されると、変数がたまたまより具体的な実行時の型を保持していても、コンパイル時にオーバーロードが修正されるため、それは問題ではありません。

あなたの方法:

public string TestExt()
{
    return this.Ext(a => a.Content);
}

コンパイル時に の 1 つの特定のオーバーロードにバインドする必要がありExtます。クラス内のT(の型)についてわかっているのは、それが に変換できるということだけなので、選択できるオーバーロードは実際には 1 つしかないため、コンパイラにとっては簡単です。a.ContentMyClass<T>object

それ以降、TestExtメソッド本体は の特定のオーバーロードを呼び出すようにハードコーディングされExtます。


編集:これはもっと簡単な例です:

static void Main()
{
    IEnumerable<object> e = new List<object>();
    var r = Generic(e);
}

static string Generic<T>(T x)
{
    return Overloaded(x);
}

static string Overloaded(IEnumerable<object> x)
{
    return "Enumerable";
}
static string Overloaded(object x)
{
    return "Object";
}

そして、あなたが今理解しているように、 にrなり"Object"ます。

T(たとえば、何らかの形で制約した場合where T : IEnumerable<object>、状況は異なります)。

これはオペレーターも同様です。たとえば、==演算子は、一般的な参照型に対しては 1 つの方法で機能し、文字列に対しては別の方法で機能するという意味でオーバーロードされています。したがって、以下の例では、オペレーターは以前から==の役割を果たします。Overloaded

static void Main()
{
    string str1 = "abc";
    string str2 = "a";
    str2 += "bc";        // makes sure this is a new instance of "abc"

    bool b1 = str1 == str2;        // true
    bool b2 = Generic(str1, str2); // false
}

static bool Generic<T>(T x, T y) where T : class
{
    return x == y;
}

どこにb2なりfalseます。

于 2013-09-03T08:07:19.590 に答える