【问题标题】:Method overloading in generic class泛型类中的方法重载
【发布时间】:2012-06-05 02:51:26
【问题描述】:

我正在使用包含以下泛型类中的重载方法的代码:

public class A<T>
{
    public void Process(T item) { /*impl*/ }
    public void Process(string item) { /*impl*/ }
}

当为string 参数化类时,我会失去使用泛型参数调用版本的可能性吗?

var a = new A<string>();
a.Process(""); //Always calls the non-generic Process(string)

【问题讨论】:

    标签: c# generics overloading


    【解决方案1】:

    特定类型优先于泛型类型。

    例如,这是我在 LINQPad 中测试的。

    void Main()
    {
        new A<string>().Process("Hello");
    }
    
    public class A<T>
    {
        public void Process(T item) { Console.WriteLine("T"); }
        public void Process(string item) { Console.WriteLine("string"); }
    }
    
    // Output: string
    

    如果您在隐藏泛型方法时遇到问题,那么您需要重新考虑一些事情。通过使用特定类型重载泛型方法,您实际上是在说:“如果需要,请使用泛型重载,但如果可以,请使用特定版本,因为它应该知道什么是最好的。

    【讨论】:

    【解决方案2】:

    我刚刚发现了一种方法,但有点眼花缭乱。因为泛型和重载在构建时得到解决,你可以定义一个泛型方法:

    public static CallerClass
    {
        public static CallGenericOverload<T>(GenericClass<T> cls, T val)
        {
            return cls.ProblemOverload(val); 
        }   
    
        //We can also make an extension method. 
        //We don't have to of course, it's just more comfortable this way.
        public static CallGenericOverloadExtension<T>(this GenericClass<T> cls, T val)
        {
            return cls.ProblemOverload(val);
        }
    
    }
    
    public GenericClass<T>
    {
         public string ProblemOverload(T val)
         {
             return "ProblemOverload(T val)";
         }
         public string ProblemOverload(string val)
         {
             return "ProblemOverload(string val)";
         }
    }
    

    现在,如果我们执行以下操作:

    var genClass = new GenericClass<string>();
    Console.WriteLine(genClass.ProblemOverload("")); //output: ProblemOverload(string val)
    Console.WriteLine(CallerClass.CallGenericOverload(genClass, "")); //output: ProblemOverload(T val)
    Console.WriteLine(genClass.CallGenericOverloadExtension("")); //output: ProblemOverload(T val)
    

    如果您定义一个泛型类而不是泛型方法,您可以使用类似的技巧。重要的是您传输到ProblemOverload 的参数需要是T 类型,而不是调用中的string 类型。毕竟,CallGenericOverload 方法知道它在构建时会得到一个T,所以它将绑定到接受参数的重载。它实际上会在运行时获得string 并不重要。

    【讨论】:

    • 既奇怪又可爱!
    【解决方案3】:

    是的。这记录在 C# 规范的第 7.5.3 节,重载决议中。

    从 7.5.3.6 开始:

    “虽然声明的签名必须是唯一的,但类型参数的替换可能会导致相同的签名。在 在这种情况下,上述重载决议的平局规则将 选择最具体的成员。”

    其中给出的示例指出,在以下情况下,G&lt;int&gt;.F1 的重载解析将选择非泛型

    class G1<U>
    {
        int F1(U u);
        int F1(int i);
    }
    

    7.5.3.2,“更好的函数成员”中概述了适用于此的平局规则:

    如果参数类型序列 {P1, P2, ..., PN} 和 {Q1, Q2, ..., QN} 是等价的(即每个 Pi 都有一个到 相应的 Qi),应用以下平局规则,在 顺序,以确定更好的函数成员。

    • 如果 MP 是非泛型方法,MQ 是泛型方法,那么 MP 优于 MQ。

    【讨论】:

    • 喜欢答案,不喜欢规则...不过幸运的是你可以显式调用方法
    【解决方案4】:

    之前做过这件事,我倾向于说“不”,但总有更多有见识的人会反对。

    如果内存够用,runtime 编译器会选择最强类型的重载来执行。

    澄清

    我的回答措辞不好,我应该投反对票。

    OP 问道,“当为字符串参数化类时,我会失去使用泛型参数调用版本的可能性吗?”我没有回答“不,你不能那样做” 但是“不,您不会失去使用通用参数调用版本的能力。”

    我应该更清楚。

    【讨论】:

    • 其实这个是在编译时解决的。运行时不选择任何东西。
    • 说得好。我以为是这样,但这就是我在咳嗽糖浆的影响下发帖的结果。 :-/
    • 嗯,重新。您的澄清:实际上您确实失去了使用通用参数调用 te 版本的能力(如果参数是字符串类型,这是 OP 要求的)。
    • 如果你像我所展示的那样明确地称呼它......不管是否有止咳糖浆......但它确实有帮助:P
    猜你喜欢
    • 2011-06-26
    • 1970-01-01
    • 2013-03-09
    • 1970-01-01
    • 1970-01-01
    • 2023-03-31
    • 1970-01-01
    • 2012-02-19
    • 1970-01-01
    相关资源
    最近更新 更多