【问题标题】:Extension method selection using generic types and Expressions使用泛型类型和表达式选择扩展方法
【发布时间】:2023-03-12 03:55:02
【问题描述】:

我有一些扩展方法使用 Expression 参数来拉入属性成员并对其进行操作,并且对于成员是 IEnumerable。但是,从泛型类内部调用时,它似乎与预期的方法重载不匹配(for r4 below)。在类之外选择了正确的方法。

这里发生了什么?这会奏效吗?还是我需要寻找新方法?

(这是在 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";
    }
}

【问题讨论】:

  • @KingKing 见r4声明旁边的评论

标签: c# generics extension-methods overloading


【解决方案1】:

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

你的方法:

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

必须在编译时绑定到Ext 的一个特定重载。由于我们所知道的关于MyClass&lt;T&gt; 类中的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"

(如果您以某种方式约束T,例如where T : IEnumerable&lt;object&gt;,情况会有所不同)。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多