C# 为什么IEnumerable<;T>;。托利斯特<;T>;()返回列表<;T>;而不是IList<;T>;?

C# 为什么IEnumerable<;T>;。托利斯特<;T>;()返回列表<;T>;而不是IList<;T>;?,c#,linq,interface,ienumerable,C#,Linq,Interface,Ienumerable,扩展方法ToList()返回一个列表。按照相同的模式,ToDictionary()返回一个字典 我很好奇为什么这些方法不分别将返回值键入IList和IDictionary。这似乎更奇怪,因为ToLookup将其返回值键入接口而不是实际实现 查看使用或其他反编译器的这些扩展方法的源代码,我们看到以下实现(显示ToList(),因为它较短): 公共静态列表ToList(此IEnumerable源代码){ if(source==null)抛出错误.ArgumentNull(“source”); 返回新

扩展方法
ToList()
返回一个
列表。按照相同的模式,
ToDictionary()
返回一个
字典

我很好奇为什么这些方法不分别将返回值键入
IList
IDictionary
。这似乎更奇怪,因为
ToLookup
将其返回值键入接口而不是实际实现

查看使用或其他反编译器的这些扩展方法的源代码,我们看到以下实现(显示
ToList()
,因为它较短):

公共静态列表ToList(此IEnumerable源代码){
if(source==null)抛出错误.ArgumentNull(“source”);
返回新列表(源);
}
那么,为什么这个方法将其返回值键入接口的特定实现,而不是接口本身呢?唯一的变化是返回类型

我很好奇,因为除了这两种情况外,
IEnumerable
扩展的签名非常一致。我总是觉得有点奇怪

此外,为了让事情变得更加混乱,国家:

根据 指定的键选择器功能

但是返回类型是
ILookup

在中,Jon Skeet提到返回类型是
List
,而不是
IList
,但没有进一步涉及主题。
广泛的搜索没有找到答案,所以我在此问你:


不将返回值作为接口键入的背后是否有任何设计决策,或者这只是偶然现象?

通常,当您在寻找具体类型的方法上调用ToList()时,否则该项可以保持为IEnumerable类型。除非您正在做需要具体列表的事情,否则不需要转换为列表。

返回
List
的优点是
List
的那些不属于
IList
的方法很容易使用。使用
列表
可以做很多事情,但使用
IList
却做不到


相比之下,
Lookup
只有一个可用的方法,
ILookup
没有(
ApplyResultSelector
),而且你可能不会最终使用它。

这些决策可能会让人觉得随意,但我猜
ToList()
返回
List
而不是接口,因为
List
都实现了
IList
,但它添加了常规
IList
类型对象中不存在的其他成员

例如,
AddRange()

查看IList应实现的功能():

公共接口IList:ICollection,
IEnumerable,IEnumerable
列表
():

公共类列表:IList、ICollection、,
IList、ICollection、IReadOnlyList、IReadOnlyCollection、IEnumerable、,
数不清
也许您自己的代码不需要
IReadOnlyList
IReadOnlyCollection
ICollection
,但.NET Framework和其他产品上的其他组件可能依赖于更专门的列表对象,这就是.NET开发团队决定不返回接口的原因


不要觉得总是返回一个接口是最好的做法。这取决于您的代码或第三方代码是否需要这样的封装。

在我看来,返回
列表
是合理的,因为方法名称是
ToList
。否则它将不得不被命名为“卫浴者”。此方法的目的就是将非特定的
IEnumerable
转换为特定类型的
列表

如果您有一个名称不明确的方法,比如
GetResults
,那么像
IList
IEnumerable
这样的返回类型对我来说似乎是合适的


如果您查看带有reflector的
查找
类的实现,您将看到许多
内部
成员,这些成员只能由LINQ本身访问。没有公共构造函数,
查找
对象是不可变的。因此,直接公开
查找
没有任何好处


Lookup
类似乎是一种LINQ内部类,并不用于公共用途。

IList相比,仅仅拥有
列表
有许多优点。首先,
List
IList
没有的方法。您还知道实现是什么,它允许您对其行为进行推理。你知道它可以有效地添加到末尾,但不是开始,你知道它的索引器非常快,等等

您不必担心您的结构被更改为
LinkedList
,从而破坏应用程序的性能。当涉及到这样的数据结构时,在相当多的上下文中,了解数据结构是如何实现的非常重要,而不仅仅是它遵循的契约。这是不应该改变的行为

您也不能将
IList
传递给接受
列表的方法,这是您经常看到的
ToList
经常被使用,因为此人确实需要一个
List
的实例来匹配他们无法控制的签名,而
IList
对此没有帮助

然后我们问自己返回
IList
有什么好处。我们可能会返回列表的其他实现,但正如前面提到的,这可能会产生非常有害的后果,几乎可以肯定,这比使用任何其他类型的列表可能获得的收益要多得多。使用接口而不是实现可能会给您带来温暖的模糊感,但即便如此
public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source) { 
   if (source == null) throw Error.ArgumentNull("source");
   return new List<TSource>(source); 
}
public interface IList<T> : ICollection<T>, 
    IEnumerable<T>, IEnumerable
public class List<T> : IList<T>, ICollection<T>, 
    IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>, 
    IEnumerable
public class List<T> : IList<T>, ICollection<T>, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>, IEnumerable{

}
public static IList<TSource> ToIList<TSource>(this IEnumerable<TSource> source)
{
    if (source == null) throw new ArgumentNullException(source);
    return source.ToList();
}
[SerializableAttribute]
public class List<T> : IList<T>, ICollection<T>, IList, ICollection, 
    IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>, IEnumerable