我对枚举器和LINQ的工作方式有些怀疑。考虑以下两个简单的选择:

List<Animal> sel = (from animal in Animals 
                    join race in Species
                    on animal.SpeciesKey equals race.SpeciesKey
                    select animal).Distinct().ToList();

or

IEnumerable<Animal> sel = (from animal in Animals 
                           join race in Species
                           on animal.SpeciesKey equals race.SpeciesKey
                           select animal).Distinct();

我更改了原始对象的名称,使其看起来像一个更通用的示例。查询本身并不那么重要。我想问的是:

foreach (Animal animal in sel) { /*do stuff*/ }

我注意到,如果我使用IEnumerable,当我调试和检查“sel”(在这种情况下是IEnumeraable)时,它有一些有趣的成员:“internal”、“outer”、“innerKeySelector”和“outerKeySelecter”,最后两个似乎是委托。“内部”成员中没有“动物”实例,而是“物种”实例,这对我来说很奇怪。我想这两位代表会决定哪一个进入,哪一个退出?我注意到,如果我使用“Distinct”,“inner”包含6项(这是不正确的,因为只有2项是Distinct),但“outer”包含正确的值。同样,可能是委托方法决定了这一点,但这比我对IEnumerable的了解要多得多。最重要的是,这两个选项中哪一个性能最佳?

通过.ToList()进行的邪恶列表转换?

或者直接使用枚举器?

如果可以的话,也请解释一下或提供一些链接来解释IEnumerable的用法。


当前回答

IEnumerable的优点是延迟执行(通常与数据库一起执行)。在实际循环数据之前,不会执行查询。这是一个等待需要的查询(也称为延迟加载)。

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

两者都有利弊。如果调用ToList,则可以消除查询何时执行的一些神秘性。如果你坚持IEnumerable,你会得到这样的好处,即程序在实际需要之前不会做任何工作。

其他回答

IEnumerable的优点是延迟执行(通常与数据库一起执行)。在实际循环数据之前,不会执行查询。这是一个等待需要的查询(也称为延迟加载)。

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

两者都有利弊。如果调用ToList,则可以消除查询何时执行的一些神秘性。如果你坚持IEnumerable,你会得到这样的好处,即程序在实际需要之前不会做任何工作。

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

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

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

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<Person> 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.

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

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

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

现在List实现IEnumerable,但表示内存中的整个集合。如果您有一个IEnumerable,并调用.ToList(),则会在内存中创建一个包含枚举内容的新列表。

linq表达式返回一个枚举,默认情况下,当您使用foreach遍历时,该表达式将执行。迭代foreach时执行IEnumerable linq语句,但可以使用.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 ) ...