C# 泛型类中的方法重载

C# 泛型类中的方法重载,c#,generics,overloading,C#,Generics,Overloading,我使用的代码在泛型类中包含以下重载方法: public class A<T> { public void Process(T item) { /*impl*/ } public void Process(string item) { /*impl*/ } } 特定类型优先于泛型类型 例如,这就是我在LINQPad中测试的内容 void Main() { new A<string>().Process("Hello"); } public cla

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

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

特定类型优先于泛型类型

例如,这就是我在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
void Main()
{
新的A()进程(“Hello”);
}
公共A类
{
公共无效进程(T项){Console.WriteLine(“T”);}
公共无效进程(字符串项){Console.WriteLine(“字符串”);}
}
//输出:字符串

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

之前已经这样做过,我倾向于说“不”,但总有更多有知识的人会提出相反的观点

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

澄清

我的回答措词不当,我应该投反对票

OP问:“当为string参数化类时,我是否失去了调用带有泛型参数的版本的可能性?”我没有回答“不,你不能这样做”,而是回答“不,你不会失去调用带有泛型参数的版本的能力。”


我应该说得更清楚。

是的。这在C#规范第7.5.3节“过载解决”中有记录

从7.5.3.6开始:

虽然声明的签名必须是唯一的,但类型参数的替换可能会导致相同的签名。在 在这种情况下,上述过载解决方案的领带断裂规则将失效 选择最具体的成员。“

其中给出的示例表明,在以下情况下,
G.F1
的重载解析将选择非泛型

class G1<U>
{
    int F1(U u);
    int F1(int i);
}
G1类
{
int F1(U);
int F1(int i);
}
7.5.3.2“更好的功能成员”中概述了此处适用的领带断裂规则:

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

  • 如果MP是非泛型方法,而MQ是泛型方法,则MP优于MQ

我刚刚发现了一种方法,但它有点斜视。由于泛型和重载在生成时得到解决,因此可以定义泛型方法:

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)";
     }
}
公共静态调用类
{
公共静态调用GenericOverload(GenericClass cls,T val)
{
返回cls.ProblemOverload(val);
}   
//我们也可以做一个扩展方法。
//当然,我们不必这样做,这样更舒服。
公共静态CallGenericOverloadExtension(此GenericClass cls,T val)
{
返回cls.ProblemOverload(val);
}
}
公共泛型类
{
公共字符串问题重载(T val)
{
返回“ProblemOverload(T val)”;
}
公共字符串问题重载(字符串值)
{
返回“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)
var genClass=new GenericClass();
Console.WriteLine(genClass.ProblemOverload(“”)//输出:ProblemOverload(字符串值)
Console.WriteLine(CallerClass.CallGenericOverload(genClass)(“”))//输出:ProblemOverload(T val)
Console.WriteLine(genClass.CallGenericOverloadExtension(“”)//输出:ProblemOverload(T val)

如果定义泛型类而不是泛型方法,则可以使用类似的技巧。重要的是,在调用中,传递到
problem重载
的参数必须是
T
类型,而不是
string
。毕竟,方法
CallGenericOverload
知道它在构建时得到
T
,因此它将绑定到接受参数的重载。它在运行时是否会得到一个
字符串
,这并不重要。

所以我认为答案是肯定的?另请参见:实际上,这是在编译时解决的。运行时没有选择任何东西。说得好。我原以为是这样的,但这就是我在咳嗽糖浆的影响下发布的内容-/嗯,是的。您的澄清:实际上您确实失去了使用泛型参数调用te version的能力(如果参数是string类型,这是OP要求的)。如果您像我所示显式调用它,则不会。。。咳嗽糖浆与否。。。但它确实有帮助:PWeird同时也很可爱!喜欢答案,不喜欢规则。。。但幸运的是,您可以明确地调用该方法
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)