为什么在两个重载都匹配时调用泛型方法?
public static void method1(object obj)
{
Console.WriteLine("Object");
}
public static void method1<T>(T t)
{
Console.WriteLine("Type T");
}
public static void Main(String args[])
{
method1("xyz"); //Will print "Type T";
}
这里不应该有任何冲突,对吧?
答案 0 :(得分:37)
通过选择最具体的过载来解决过载问题。在这种情况下,method1<string>(string)
比method1(object)
更具体,因此选择了过载。
section 7.4.2 of the C# specification中有详细信息。
如果要选择特定的重载,可以通过将参数显式地转换为所需的类型来实现。以下内容将调用method1(object)
重载而不是通用的重载:
method1((object)"xyz");
有些情况下,编译器不知道要选择哪个重载,例如:
void method2(string x, object y);
void method2(object x, string y);
method2("xyz", "abc");
在这种情况下,编译器不知道要选择哪个重载,因为两个重载都没有明显好于另一个(它不知道哪个字符串隐式地向下转换为对象)。所以它会发出编译错误。
答案 1 :(得分:11)
C#总是会选择最具体的方法。
编译时
method1("xyz");
它将查找具有指定名称的所有方法,然后尝试匹配参数。编译器将选择最具体的方法,在这种情况下,它更喜欢
method1(string s)
在
method1<T>(T t) with T = string
最后
method1(object o)
请注意@ Erik对编译器无法决定的例子的优秀答案。
答案 2 :(得分:2)
因为您已经将T作为参数传递,所以您不需要输入method1<string>("xyz");
就可以method1("xyz");
,。。Net已经知道它是一个字符串。如果你有方法1那么这将是一个不同的故事。
此外,由于method1(object obj)
不接受字符串作为参数,因此它首先支持泛型函数,它可以推断T.如果您要将method1(object obj)
更改为method1(string obj)
它首先会支持通用。
答案 3 :(得分:0)
要查找调用方法的匹配签名,编译器将在虚拟表中从下至上搜索类型层次结构:
因为类在接口上占主导地位。
实际上,在成为接口类型之前,对象首先是类的类型。
非通用签名优先于通用,事实和事实优先于抽象,除非使用通用参数允许对更特殊类型的实例进行调用。
此电话:
method1("xyz");
完美匹配:
void method1<T>(T t) { }
匹配之前:
void method1(object obj)
因为字符串是一个特殊的对象,可以将其用作通用参数以使其更加准确。
另一方面,如果您写:
void method1(string obj) { }
void method1<T>(T t) { }
第一种方法就是这样。
var instance = new List<string>();
MyMethod(instance);
MyMethod((IEnumerable<string>) instance);
MyMethod<string>(instance);
MyMethod((object)instance);
void MyMethod<T>(List<T> instance) { }
void MyMethod<T>(IEnumerable<T> list) { }
void MyMethod<T>(T instance) { }
void MyMethod(object instance) { }
第一次调用将调用第一个方法,因为实例是List的类型(类型匹配)。
第二次调用由于侧面转换(实现)而调用第二种方法。
第三个调用调用第三个方法,因为指定了要作用于(模板)的通用参数。
由于向下转换(多态性),第四个调用调用第四个方法。