使用泛型类型和表达式的扩展方法选择

本文关键字:扩展 方法 选择 表达式 泛型类型 | 更新日期: 2023-09-27 18:29:55

我有一些扩展方法,它们使用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";
    }
}

使用泛型类型和表达式的扩展方法选择

泛型不是动态类型。要调用的重载在编译时被冻结。当程序稍后运行时,即使变量恰好包含更特定的运行时类型,这也无关紧要,因为重载在编译时已经修复。

您的方法:

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

必须在编译时绑定到CCD_ 1的一个特定重载。由于我们所知道的MyClass<T>类中的Ta.Content的类型)是可转换为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"

(如果以某种方式约束Ext0,例如where T : IEnumerable<object>,情况就会有所不同)。

操作员也是如此。例如,==运算符是重载的,因为它以一种方式适用于一般引用类型,而以另一种方式则适用于字符串。因此,在下面的例子中,操作员==扮演了之前的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;
}

其中CCD_ 15变为CCD_。