泛型类中的方法重载

本文关键字:重载 方法 泛型类 | 更新日期: 2023-09-27 18:35:43

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

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)

泛型类中的方法重载

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

例如,这是我在 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

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

我刚刚发现了一种方法,但它有点斗鸡眼。由于泛型和重载是在生成时解决的,因此可以定义一个泛型方法:

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并不重要。

是的。这记录在 C# 规范第 7.5.3 节 重载解析中。

从 7.5.3.6 开始:

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

其中给出的示例指出,在下面的情况下,G<int>.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。

以前做过这个,我倾向于说"不",但总有更多的知识渊博的人会反驳。

如果内存可用,运行时编译器将选择要执行的最强类型的重载。

澄清

我的回答措辞糟糕,我应该投反对票。

OP 问道:"在参数化字符串类时,我是否失去了使用泛型参数调用版本的可能性?我不是在回答"不,你不能那样做",而是回答"不,你不会失去使用泛型参数调用版本的能力。

我应该更清楚。