c# - IEnumerable vs List - 使用什么?它们是如何工作的?

c# - IEnumerable vs List - 使用什么?它们是如何工作的?

11 回答 11

This answer is useful

845

IEnumerable描述行为,而 List 是该行为的实现。当你使用 时IEnumerable,你让编译器有机会将工作推迟到以后,可能会在此过程中进行优化。如果您使用 ToList(),您会强制编译器立即具体化结果。

每当我“堆叠”LINQ 表达式时,我都会使用IEnumerable,因为仅通过指定行为我给 LINQ 一个推迟评估并可能优化程序的机会。还记得 LINQ 是如何在您枚举之前不生成查询数据库的 SQL 的吗?考虑一下:

public IEnumerable AllSpotted()

{

return from a in Zoo.Animals

where a.coat.HasSpots == true

select a;

}

public IEnumerable Feline(IEnumerable sample)

{

return from a in sample

where a.race.Family == "Felidae"

select a;

}

public IEnumerable Canine(IEnumerable sample)

{

return from a in sample

where a.race.Family == "Canidae"

select a;

}

现在您有了一个选择初始样本(“AllSpotted”)以及一些过滤器的方法。所以现在你可以这样做:

var Leopards = Feline(AllSpotted());

var Hyenas = Canine(AllSpotted());

那么使用 List over 更快IEnumerable吗?仅当您想防止查询被多次执行时。但总体来说更好吗?在上面,Leopards 和 Hyenas 分别被转换为单个 SQL 查询,并且数据库只返回相关的行。但是如果我们从 中返回了一个列表AllSpotted(),那么它可能会运行得更慢,因为数据库返回的数据可能比实际需要的多得多,而且我们浪费了在客户端进行过滤的周期。

在程序中,将查询转换为列表可能会更好,直到最后,所以如果我要多次列举 Leopards 和 Hyenas,我会这样做:

List Leopards = Feline(AllSpotted()).ToList();

List Hyenas = Canine(AllSpotted()).ToList();

于 2010-09-02T15:36:42.273 回答

This answer is useful

268

这里有一篇非常好的文章:Claudio Bernasconi 的 TechBlog:何时使用 IEnumerable、ICollection、IList 和 List

这里有一些关于场景和功能的基础知识点:

于 2015-06-04T14:07:30.187 回答

This answer is useful

151

实现的类IEnumerable允许您使用foreach语法。

基本上它有一种方法来获取集合中的下一个项目。它不需要整个集合都在内存中,也不知道其中有多少项目,foreach只是不断获取下一个项目直到用完。

这在某些情况下非常有用,例如在一个庞大的数据库表中,您不想在开始处理行之前将整个数据复制到内存中。

现在List实现IEnumerable,但代表内存中的整个集合。如果你有一个IEnumerable并且你打电话给.ToList()你创建一个新列表,其中包含内存中的枚举内容。

您的 linq 表达式返回一个枚举,默认情况下,当您使用foreach. 迭代 时会执行IEnumerablelinq 语句foreach,但您可以使用.ToList().

这就是我的意思:

var things =

from item in BigDatabaseCall()

where ....

select item;

// this will iterate through the entire linq statement:

int count = things.Count();

// this will stop after iterating the first one, but will execute the linq again

bool hasAnyRecs = things.Any();

// this will execute the linq statement *again*

foreach( var thing in things ) ...

// this will copy the results to a list in memory

var list = things.ToList()

// this won't iterate through again, the list knows how many items are in it

int count2 = list.Count();

// this won't execute the linq statement - we have it copied to the list

foreach( var thing in list ) ...

于 2010-09-02T15:24:35.903 回答

This answer is useful

111

没有人提到一个关键的区别,讽刺地回答了一个作为重复的问题而结束的问题。

IEnumerable 是只读的,而 List 不是。

请参阅List 和 IEnumerable 之间的实际区别

于 2014-12-09T08:30:32.657 回答

This answer is useful

72

要意识到的最重要的事情是,使用 Linq,查询不会立即得到评估。它仅作为迭代结果的一部分运行IEnumerable-foreach这就是所有奇怪的代表正在做的事情。

因此,第一个示例通过调用ToList并将查询结果放入列表中来立即评估查询。

第二个示例返回一个IEnumerable包含稍后运行查询所需的所有信息的一个。

在性能方面,答案是视情况而定。如果您需要立即评估结果(例如,您正在改变稍后查询的结构,或者如果您不希望迭代IEnumerable花费很长时间)使用列表。否则使用IEnumerable. 默认应该是使用第二个示例中的按需评估,因为这通常使用较少的内存,除非有特定原因将结果存储在列表中。

于 2010-09-02T15:08:23.043 回答

This answer is useful

45

IEnumerable 的优点是延迟执行(通常使用数据库)。在您实际循环遍历数据之前,不会执行查询。这是一个等待直到需要它的查询(又名延迟加载)。

如果您调用 ToList,查询将被执行,或者如我所说的“物化”。

两者都有优点和缺点。如果您调用 ToList,您可能会消除关于何时执行查询的一些谜团。如果您坚持使用 IEnumerable,您将获得该程序在实际需要之前不会执行任何工作的优势。

于 2010-09-02T15:13:22.393 回答

This answer is useful

34

我将分享我有一天陷入的一个被误用的概念:

var names = new List {"mercedes", "mazda", "bmw", "fiat", "ferrari"};

var startingWith_M = names.Where(x => x.StartsWith("m"));

var startingWith_F = names.Where(x => x.StartsWith("f"));

// updating existing list

names[0] = "ford";

// Guess what should be printed before continuing

print( startingWith_M.ToList() );

print( startingWith_F.ToList() );

预期结果

// I was expecting

print( startingWith_M.ToList() ); // mercedes, mazda

print( startingWith_F.ToList() ); // fiat, ferrari

实际结果

// what printed actualy

print( startingWith_M.ToList() ); // mazda

print( startingWith_F.ToList() ); // ford, fiat, ferrari

解释

根据其他答案,结果的评估被推迟到调用ToList或类似的调用方法,例如ToArray。

所以我可以将这种情况下的代码重写为:

var names = new List {"mercedes", "mazda", "bmw", "fiat", "ferrari"};

// updating existing list

names[0] = "ford";

// before calling ToList directly

var startingWith_M = names.Where(x => x.StartsWith("m"));

var startingWith_F = names.Where(x => x.StartsWith("f"));

print( startingWith_M.ToList() );

print( startingWith_F.ToList() );

玩转

https://repl.it/E8Ki/0

于 2016-11-26T08:03:35.067 回答

This answer is useful

16

如果您只想枚举它们,请使用IEnumerable.

但是请注意,更改被枚举的原始集合是一项危险的操作 - 在这种情况下,您将ToList首先想要这样做。这将为内存中的每个元素创建一个新的列表元素,枚举IEnumerable并且如果您只枚举一次,则性能会降低 - 但更安全,有时这些List方法很方便(例如在随机访问中)。

于 2010-09-02T15:09:08.937 回答

This answer is useful

7

除了上面发布的所有答案之外,这是我的两分钱。除了 List 之外,还有许多其他类型实现了 IEnumerable,例如 ICollection、ArrayList 等。因此,如果我们将 IEnumerable 作为任何方法的参数,我们可以将任何集合类型传递给函数。即我们可以有方法来操作抽象而不是任何特定的实现。

于 2017-06-15T23:22:32.297 回答

This answer is useful

2

在很多情况下(例如无限列表或非常大的列表)IEnumerable 无法转换为列表。最明显的例子是所有的素数,facebook 的所有用户及其详细信息,或者 ebay 上的所有项目。

不同之处在于“列表”对象存储“此时此地”,而“IEnumerable”对象“一次只工作一个”。因此,如果我浏览 ebay 上的所有项目,即使是小型计算机也能处理一次,但“.ToList()”肯定会让我内存不足,无论我的计算机有多大。没有一台计算机可以单独包含和处理如此大量的数据。

[编辑] - 不用说 - 这不是“这个或那个”。通常在同一个类中同时使用列表和 IEnumerable 会很有意义。世界上没有一台计算机可以列出所有素数,因为根据定义,这将需要无限量的内存。但是你可以很容易地想到 aclass PrimeContainer其中包含一个

IEnumerable primes,由于显而易见的原因,它也包含一个SortedList _primes。到目前为止计算的所有素数。下一个要检查的素数只会针对现有素数(直到平方根)运行。这样你就可以同时获得一个素数(IEnumerable)和一个很好的“迄今为止的素数”列表,这是整个(无限)列表的一个很好的近似值。

于 2019-03-11T08:06:20.463 回答

This answer is useful

2

IEnumerable(延迟执行)的缺点是,在您调用.ToList()列表之前,列表可能会发生变化。对于一个非常简单的例子 - 这会工作

var persons;

using (MyEntities db = new MyEntities()) {

persons = db.Persons.ToList(); // It's mine now. In the memory

}

// do what you want with the list of persons;

这行不通

IEnumerable persons;

using (MyEntities db = new MyEntities()) {

persons = db.Persons; // nothing is brought until you use it;

}

persons = persons.ToList(); // trying to use it...

// but this throws an exception, because the pointer or link to the

// database namely the DbContext called MyEntities no longer exists.

于 2021-10-12T12:31:29.097 回答

相关文章

极光颜色

极光颜色

2025-10-19 阅读 4855
如何提高打字速度:完整指南
千万别碰CSGO饰品交易!除非你看完这篇赚钱攻略