关于泛型和 IEnumerable 的方法重载解决方案

Posted

技术标签:

【中文标题】关于泛型和 IEnumerable 的方法重载解决方案【英文标题】:Method overload resolution with regards to generics and IEnumerable 【发布时间】:2011-06-22 01:18:20 【问题描述】:

前几天我注意到了,说你有两个重载的方法:

public void Print<T>(IEnumerable<T> items) 
    Console.WriteLine("IEnumerable T"); 

public void Print<T>(T item) 
    Console.WriteLine("Single T"); 

这段代码:

public void TestMethod()   
    var persons = new[]  
        new Person  Name = "Yan", Age = 28 ,
        new Person  Name = "Yinan", Age = 28  
    ;  
    Print(persons);
    Print(persons.ToList()); 

打印:

Single T
Single T

为什么在这些情况下Person[]List&lt;Person&gt;T 的匹配比与IEnumerable&lt;T&gt; 的匹配更好?

谢谢,

更新: 另外,如果你有另一个超载

public void Print<T>(List<T> items) 
    Console.WriteLine("List T");

Print(persons.ToList()); 实际上会打印List T 而不是Single T

【问题讨论】:

调用Print(persons as IEnumerable&lt;Person&gt;); 应该调用其他方法。泛型方法查找不会执行从 Person[]IEnumerable&lt;Person&gt; 的隐式转换。当您调用 person.ToList() 时,您的直接类型是 List&lt;Person&gt;(也不需要隐式转换)。 +1 我一直在摆弄这个问题。 【参考方案1】:

您问题的第一部分(没有特定于 List 的重载)很简单。让我们考虑 Array 调用,因为它对两个调用的工作方式相同:

首先,类型推断产生两种可能的调用通用实现:Print&lt;Person[]&gt;(Person[] items)Print&lt;Person&gt;(IEnumerable&lt;Person&gt; items)

然后重载决议开始,第一个获胜,因为第二个需要隐式转换,而第一个不需要(参见 C# 规范的第 7.4.2.3 节)。相同的机制适用于 List 变体。

通过添加重载,使用 List 调用生成第三个可能的重载:Print&lt;Person&gt;(List&lt;Person&gt; items)。参数与Print&lt;List&lt;Person&gt;&gt;(List&lt;Person&gt; items) 相同,但同样,第 7.4.3.2 节提供了该语言的解决方案

递归地,如果至少一个类型参数更具体并且没有类型参数比另一个类型参数中的相应类型参数更具体,则构造类型比另一个构造类型(具有相同数量的类型参数)更具体。

因此,Print&lt;Person&gt; 重载比 Print&lt;List&lt;Person&gt;&gt; 重载更具体,并且 List 版本胜过 IEnumerable,因为它不需要隐式转换。

【讨论】:

感谢您提到隐式转换,现在一切都说得通了【参考方案2】:

因为从泛型Print(Person[] item)Print(List&lt;Person&gt; item) 生成的方法比IEnumerable&lt;T&gt; 更匹配。

编译器会根据您的类型参数生成这些方法,因此通用模板Print&lt;T&gt;(T item) 将被编译为Print(Person[] item)Print(List&lt;Person&gt; item)(好吧,在编译时无论类型代表List&lt;Person&gt;)。因此,编译器会将方法调用解析为接受直接类型的特定方法,而不是Print(IEnumerable&lt;Peson&gt;)的实现。

【讨论】:

换句话说, Print 将始终被视为最佳匹配,因为 T 可以是任何东西?但是,如果您添加一个 Print(List items) 方法,那将是为 Print(persons.ToList) 调用的方法 另外,根据 Eric Lippert 的这篇博文,这不是 C# 泛型的工作方式blogs.msdn.com/b/ericlippert/archive/2009/07/30/… 你所描述的是 C++ 中的模板

以上是关于关于泛型和 IEnumerable 的方法重载解决方案的主要内容,如果未能解决你的问题,请参考以下文章

打字稿、泛型和重载

返回一个通用的 IEnumerable<T>

泛型和枚举

C# 使用泛型重载:错误还是特性?

泛型和强制转换 - 不能将继承的类强制转换为基类

C# 泛型和类型检查