且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

IEnumerable 与列表 - 使用什么?它们是如何工作的?

更新时间:2023-11-22 23:23:40

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

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

public IEnumerableAllSpotted(){从 Zoo.Animals 中的 a 返回其中 a.coat.HasSpots == true选择一个;}公共 IEnumerable猫科动物(IEnumerable 样本){从样本中返回其中 a.race.Family == "猫科"选择一个;}公共 IEnumerable犬类(IEnumerable 样本){从样本中返回其中 a.race.Family == 犬科"选择一个;}

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

var Leopards = Feline(AllSpotted());var Hyenas = Canine(AllSpotted());

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

在程序中,***推迟到最后才将查询转换为列表,因此如果我要不止一次枚举 Leopards 和 Hyenas,我会这样做:

ListLeopards = Feline(AllSpotted()).ToList();列出鬣狗 = Canine(AllSpotted()).ToList();

I have some doubts over how Enumerators work, and LINQ. Consider these two simple selects:

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();

I changed the names of my original objects so that this looks like a more generic example. The query itself is not that important. What I want to ask is this:

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

  1. I noticed that if I use IEnumerable, when I debug and inspect "sel", which in that case is the IEnumerable, it has some interesting members: "inner", "outer", "innerKeySelector" and "outerKeySelector", these last 2 appear to be delegates. The "inner" member does not have "Animal" instances in it, but rather "Species" instances, which was very strange for me. The "outer" member does contain "Animal" instances. I presume that the two delegates determine which goes in and what goes out of it?

  2. I noticed that if I use "Distinct", the "inner" contains 6 items (this is incorrect as only 2 are Distinct), but the "outer" does contain the correct values. Again, probably the delegated methods determine this but this is a bit more than I know about IEnumerable.

  3. Most importantly, which of the two options is the best performance-wise?

The evil List conversion via .ToList()?

Or maybe using the enumerator directly?

If you can, please also explain a bit or throw some links that explain this use of IEnumerable.

IEnumerable describes behavior, while List is an implementation of that behavior. When you use IEnumerable, you give the compiler a chance to defer work until later, possibly optimizing along the way. If you use ToList() you force the compiler to reify the results right away.

Whenever I'm "stacking" LINQ expressions, I use IEnumerable, because by only specifying the behavior I give LINQ a chance to defer evaluation and possibly optimize the program. Remember how LINQ doesn't generate the SQL to query the database until you enumerate it? Consider this:

public IEnumerable<Animals> AllSpotted()
{
    return from a in Zoo.Animals
           where a.coat.HasSpots == true
           select a;
}

public IEnumerable<Animals> Feline(IEnumerable<Animals> sample)
{
    return from a in sample
           where a.race.Family == "Felidae"
           select a;
}

public IEnumerable<Animals> Canine(IEnumerable<Animals> sample)
{
    return from a in sample
           where a.race.Family == "Canidae"
           select a;
}

Now you have a method that selects an initial sample ("AllSpotted"), plus some filters. So now you can do this:

var Leopards = Feline(AllSpotted());
var Hyenas = Canine(AllSpotted());

So is it faster to use List over IEnumerable? Only if you want to prevent a query from being executed more than once. But is it better overall? Well in the above, Leopards and Hyenas get converted into single SQL queries each, and the database only returns the rows that are relevant. But if we had returned a List from AllSpotted(), then it may run slower because the database could return far more data than is actually needed, and we waste cycles doing the filtering in the client.

In a program, it may be better to defer converting your query to a list until the very end, so if I'm going to enumerate through Leopards and Hyenas more than once, I'd do this:

List<Animals> Leopards = Feline(AllSpotted()).ToList();
List<Animals> Hyenas = Canine(AllSpotted()).ToList();