首页 文章

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

提问于
浏览
560

我对枚举器如何工作以及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*/ }
  • 我注意到如果我使用 IEnumerable ,当我调试并检查"sel",在这种情况下是IEnumerable时,它有一些有趣的成员:"inner","outer","innerKeySelector"和"outerKeySelector",这些最后2个似乎是委托 . "inner"成员中没有"Animal"实例,而是"Species"实例,这对我来说非常奇怪 . "outer"成员确实包含"Animal"实例 . 我假设两位代表确定哪些进入,哪些进出?

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

  • 最重要的是,两个选项中哪一个是表现最好的?

邪恶的名单通过 .ToList() 转换?

或者直接使用枚举器?

如果可以,请解释一下或抛出一些解释这种IEnumerable使用的链接 .

9 回答

  • 630

    IEnumerable的优点是延迟执行(通常使用数据库) . 在实际循环数据之前,查询将不会执行 . 这是一个等待它需要的查询(也就是延迟加载) .

    如果你调用ToList,查询将被执行,或者如我所愿“实现” .

    两者都有利弊 . 如果你调用ToList,你可能会删除一些关于何时执行查询的谜团 . 如果你坚持IEnumerable,你会得到这样的好处,即程序在实际需要之前不会做任何工作 .

  • 65

    没有人提到一个至关重要的区别,具有讽刺意味的回答是一个被视为重复的问题 .

    IEnumerable是只读的而List不是 .

    Practical difference between List and IEnumerable

  • 114

    有一篇非常好的文章:Claudio Bernasconi的TechBlog:When to use IEnumerable, ICollection, IList and List

    Here some basics points about scenarios and functions:

    enter image description here

    enter image description here

  • 15

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

    每当我使用"stacking" 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超过 IEnumerable 会更快吗?仅当您要阻止查询多次执行时 . 但总体来说它更好吗?在上面,Leopards和Hyenas每个都被转换为单个SQL查询,数据库只返回相关的行 . 但是如果我们从 AllSpotted() 返回了一个List,那么它可能会运行得更慢,因为数据库可能返回的数据远远超过实际需要,并且我们浪费了在客户端进行过滤的循环 .

    在一个程序中,最好将查询推迟到列表直到最后,所以如果我要通过Leopards和Hyenas多次枚举,我会这样做:

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

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

  • 15

    实现 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 ) ...
    
  • 36

    最重要的是要实现,使用Linq,查询不会立即得到评估 . 它只是在 foreach 中迭代结果 IEnumerable<T> 的一部分运行 - 这就是所有奇怪的代表正在做的事情 .

    因此,第一个示例通过调用 ToList 立即评估查询并将查询结果放入列表中 .
    第二个示例返回 IEnumerable<T> ,其中包含稍后运行查询所需的所有信息 .

    在性能方面,答案取决于它 . 如果您需要立即评估结果(例如,稍后再查询're mutating the structures you',或者如果您不希望迭代超过 IEnumerable<T> 需要很长时间),请使用列表 . 否则使用 IEnumerable<T> . 默认值应该是在第二个示例中使用按需评估,因为通常使用较少的内存,除非有特定的原因将结果存储在列表中 .

  • 64

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

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

  • 115

    我将分享一下我有一天误入歧途的概念:

    var names = new List<string> {"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<string> {"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() );
    

    玩arround

    https://repl.it/E8Ki/0

相关问题