泛型类中的方法重载

10

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

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)
4个回答

14

具体类型优先于通用类型。

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

如果你对隐藏通用方法存在问题,那么你需要重新思考一下。通过使用特定类型重载通用方法,你实际上是在说:“如果需要的话,请使用通用重载,但如果可以,请使用特定版本,因为它应该知道哪个最好。”


那我就理解为答案是肯定的了? - mellamokb
参见:http://blogs.msdn.com/b/ericlippert/archive/2009/07/30/generics-are-not-templates.aspx - Michael Edenfield

5
是的。这在C#规范第7.5.3节“重载决策”中有所说明。
从7.5.3.6可得:
“虽然声明的签名必须是唯一的,但替换类型参数可能会导致相同的签名。在这种情况下,上面的重载解析决策规则将选择最具体的成员。”
其中给出的示例表明,在以下情况下,对于G.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更好。

喜欢答案,不喜欢规则...但幸运的是你可以显式地调用该方法。 - Jay

5

我刚刚发现了一种方法,但它有点复杂。由于泛型和重载是在构建时解决的,因此您可以定义一个泛型方法:

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

奇怪但又可爱! - Jay

0

我之前做过这个,我倾向于说“不行”,但总有更有经验的人会持不同意见。

如果我的记忆没错,运行时编译器会选择执行最强类型的重载。

澄清

我的回答措辞不当,我应该受到负评。

OP问道:“当为字符串类参数化时,我是否失去了调用具有通用参数的版本的可能性?” 我的回答并不是“不,你不能这样做”,而是“不,你不会失去调用具有通用参数的版本的能力。”

我应该表述得更清楚。


2
实际上,这在编译时就解决了。运行时不会选择任何东西。 - user703016
说得好。我也认为是这样,但这就是我在喝咳嗽糖浆的情况下发布帖子的后果。 :-/ - Mike Hofer
嗯,关于您的澄清:实际上,如果参数是字符串类型(这是OP所问的),您确实会失去调用具有通用参数的版本的能力。 - jeroenh
不过,如果你按照我展示的方式显式地调用它,无论是止咳糖浆还是其他什么都可以... 但这确实有帮助 :P - Jay

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接