协慌网

登录 贡献 社区

IEnumerable vs List - 使用什么?它们如何运作?

我对 Enumerators 和 LINQ 的工作方式有疑问。考虑以下两个简单选择:

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

或者

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*/ }
  1. 我注意到,如果我使用IEnumerable ,则在调试和检查 “sel”(在这种情况下就是 IEnumerable)时,它具有一些有趣的成员:“inner”,“outer”,“innerKeySelector” 和 “outerKeySelector”,这最后两个似乎是代表。 “内部” 成员中没有 “动物” 实例,而是 “物种” 实例,这对我来说很奇怪。 “外部”成员确实包含 “动物” 实例。我假设这两个代表确定哪个进出什么?

  2. 我注意到,如果我使用 “Distinct”,则 “inner” 包含 6 个项目(这是不正确的,因为只有 2 个是 Distinct),但是 “outer” 确实包含正确的值。同样,可能委托方法确定了这一点,但这比我对 IEnumerable 的了解还多。

  3. 最重要的是,这两个选项中哪个是最佳性能方面的?

.ToList()邪恶列表转换?

还是直接使用枚举器?

如果可以的话,也请稍作解释或抛出一些链接,以解释 IEnumerable 的这种用法。

答案

IEnumerable描述行为,而 List 是该行为的实现。使用IEnumerable ,可以使编译器有机会将工作推迟到以后,可能会一直进行优化。如果使用 ToList(),则强制编译器立即对结果进行校验。

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

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;
}

现在,您有了一个选择初始样本(“AllSpotted”)以及一些过滤器的方法。现在,您可以执行以下操作:

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

那么使用 List over IEnumerable是否更快?仅当您要阻止查询多次执行时。但是总体上更好吗?在上面的代码中,Leopards 和 Hyenas 分别转换为单个 SQL 查询,并且数据库仅返回相关的行。但是,如果我们从AllSpotted()返回了 List,则它的运行速度可能会变慢,因为数据库返回的数据可能远远超过实际需要的数据,并且浪费了在客户端进行过滤的周期。

在程序中,最好将查询转换为列表直到最后,最好,因此,如果我要多次通过 Leopards 和 Hyenas 枚举,我可以这样做:

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

有一篇非常好的文章由:Claudio Bernasconi 的 TechBlog 撰写: 何时使用 IEnumerable,ICollection,IList 和 List

这里是有关方案和功能的一些基本知识:

在此处输入图片说明在此处输入图片说明

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

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

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

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

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