C# 什么';这是IEnumerable的角色<;T>;我为什么要用它?

C# 什么';这是IEnumerable的角色<;T>;我为什么要用它?,c#,collections,ienumerable,C#,Collections,Ienumerable,当我可以使用……比如说列表时,为什么要使用IEnumerable?前者比后者有什么优势?IEnumerable为您提供了实现自己的对象集合存储和迭代逻辑的方法IEnumerable是一个接口,它告诉我们可以枚举一系列T实例。如果需要允许某人查看集合中的每个对象并对其执行某些操作,这就足够了 另一方面,List是IEnumerable的一个特定实现,它以特定的已知方式存储对象。在内部,这可能是存储通过IEnumerable公开的值的一种非常好的方法,但是列表并不总是合适的。例如,如果您不需要按索引

当我可以使用……比如说
列表时,为什么要使用
IEnumerable
?前者比后者有什么优势?

IEnumerable为您提供了实现自己的对象集合存储和迭代逻辑的方法
IEnumerable
是一个接口,它告诉我们可以枚举一系列
T
实例。如果需要允许某人查看集合中的每个对象并对其执行某些操作,这就足够了

另一方面,
List
IEnumerable
的一个特定实现,它以特定的已知方式存储对象。在内部,这可能是存储通过
IEnumerable
公开的值的一种非常好的方法,但是
列表并不总是合适的。例如,如果您不需要按索引访问项目,而是在集合的开头不断插入项目,然后从末尾删除项目,则使用
队列
更合适


通过在API中使用
IEnumerable
,您可以灵活地随时更改内部实现,而无需更改任何其他代码。这在允许代码灵活且可维护方面具有巨大的好处。

集合的不同实现可以枚举;使用IEnumerable可以清楚地表明,您感兴趣的是可枚举性,而不是集合的底层实现的结构


正如Copsey先生所提到的,这有利于提供与实现的解耦,但我的观点是,尽可能明确定义接口功能的最小子集(即,尽可能使用IEnumerable而不是List)提供精确的解耦,同时也需要适当的设计理念。也就是说,您可以实现解耦,但不能实现最小依赖,但如果不实现最大解耦,就无法实现最小依赖。

通常您不会直接使用
IEunumerable
。它是许多您更可能使用的其他集合的基类<例如,code>IEnumerable
提供了使用
foreach
在集合中循环的功能。许多继承类(如
List
)都使用它。但是
IEnumerable
不提供排序方法(尽管您可以使用Linq),而其他一些通用集合,如
List
也有该方法


当然,您可以使用它创建自己的自定义集合类型。但是对于日常的东西,它可能没有从它派生的集合那么有用。

如果你计划构建一个公共API,最好使用
IEnumerable
而不是
List
,因为你最好使用最简单的接口/类<代码>列表
允许您按索引访问对象(如果需要)


当使用
IEnumerable
ICollection
List
等时,这是一个很好的指导原则。

使用迭代器的概念,您可以在速度和内存使用方面显著提高算法质量

让我们考虑下面两个代码示例。两者都解析文件,一个在集合中存储行,另一个使用可枚举

第一个例子是O(N)时间和O(N)内存:

以下是SelectLines()的代码

IEnumerable SelectLines()
{
...
使用(变量读取器=…)
而((line=reader.ReadLine())!=null)
收益率回归线;
}
这就是为什么它加载的项目平均比第一个示例少两倍。假设在文件范围内的任何位置找到元素的概率是相同的。对于IEnumerable,只从文件中读取与感兴趣的元素对应的行。如果对枚举进行ToList调用,甚至在开始搜索之前都会读取整个文件


当然,第一个示例中的列表将保存内存中的所有项,这就是O(N)内存使用的原因。

IEnumerable的实现通常是一个类的首选方式,用于指示它应该可以与“foreach”循环一起使用,并且同一对象上的多个“foreach”循环应该独立运行。尽管IEnumerable除了“foreach()”,还有其他用法,但实现IEnumerable的正常指示是,该类在classItem{foo.do_something();}中说“foreach foo”是有意义的。

关于这一点,我们写道:

在声明方法的参数类型时,应指定最薄弱的类型, 优先选择接口而不是基类。例如,如果您正在编写一个 操作一组项时,最好通过 使用诸如
IEnumerable
之类的接口,而不是使用诸如
List
之类的强数据类型,或者甚至使用诸如
ICollection
IList
之类的强接口类型:

//需要:此方法使用弱参数类型
公共空操作项(IEnumerable集合){…}
//不需要:此方法使用强参数类型
公共无效操作项(列表集合){…}
当然,原因是有人可以调用传入数组对象的第一个方法、
List
对象、
String
对象等等—任何类型实现
IEnumerable
的对象。第二个方法只允许传入
List
对象;它不接受数组或
String
当然,如果您正在编写一个需要列表(而不仅仅是任何可枚举对象)的方法,那么您应该声明参数
IEnumerable<string> lines = SelectLines();
List<Item> items = lines.Select(l=>ParseToItem(l)).ToList();
var itemOfIterest = items.FirstOrDefault(IsItemOfIterest); 
var itemOfIterest = lines.FirstOrDefault(l=>IsItemOfIterest(ParseToItem(l));
 IEnumerable<string> SelectLines()
 {
  ...
  using(var reader = ...)
  while((line=reader.ReadLine())!=null)
   yield return line;
 }
// Desired: This method uses a weak parameter type   
public void ManipulateItems<T>(IEnumerable<T> collection) { ... }  

// Undesired: This method uses a strong parameter type   
public void ManipulateItems<T>(List<T> collection) { ... }
public class Car :Vehicle
{
}

private void doSomething1(IEnumerable<Vehicle> vehicles)
{

}

private void doSomething2(List<Vehicle> vehicles)
{

}

var vec = new List<Car>();
doSomething1(vec); // this is ok 
doSomething2(vec); // this will give a compilation error