关于C#:IEnumerable vs list的用法是什么

IEnumerable vs List - What to Use? How do they work?

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

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

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

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

1
foreach (Animal animal in sel) { /*do stuff*/ }
  • 我注意到,如果我使用IEnumerable,当我调试和检查"sel"(在这种情况下是IEnumerable)时,它有一些有趣的成员:"inner"、"outer"、"innerkeyselector"和"outerkeyselector",最后2个看起来是委托。"内在"成员没有"动物"的例子,而是"物种"的例子,这对我来说很奇怪。"外部"成员确实包含"动物"实例。我想这两位代表决定了哪一位代表进入,哪一位代表退出?

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

  • 最重要的是,这两个选项中哪一个是性能最佳的?

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

    或者直接使用枚举器?

    如果可以的话,请解释一下,或者抛出一些链接来解释IEnumerable的这种用法。


    IEnumerabledescribes战略行为,但这是一个执行的行为。当你使用IEnumerable,给你一个机会去编译工作Defer直到后来,possibly沿着优化的方法。如果你使用你的tolist(力)的两个reify编译结果了吧。

    每当我在"stacking"LINQ表达式中使用,只有IEnumerable,因为城市的行为specifying LINQ提供一个机会去possibly OPTIMIZE Defer和评价的程序。我记得我没有LINQ to Generate SQL的两个查询的数据库,直到你枚举它吗?考虑这个:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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;
    }

    现在你有一个方法,selects初始样品("allspotted"),加上一些滤波器。所以现在你可以这样做:

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

    所以它是禁食两IEnumerable使用清单吗?如果你想要的只是一个查询,从预防executed有超过一次。但它是更好的整体。嗯,在以上,leopards get和hyenas转换成SQL查询每个单身,和只读数据库返回的行,是相关的。但如果我们返回一个仇恨列表,从AllSpotted(),那么它可能运行的数据库可能因为slower回报父亲更多的数据比我需要的冰,和我们的废物循环做滤波的客户端。

    在一个程序,它可能是更好的两个查询Defer转换你的诡计,直到两个极比,所以如果我要枚举的通leopards和hyenas超过一次,这样做的:

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


    有一条很好的书面市:Claudio贝尔纳斯科尼的techblog这里:当使用IEnumerable,ICollection,IList和战略

    这里是一些基础知识点的情景和功能:

    enter image description here enter image description here


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

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

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

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

    Linq表达式返回一个枚举,默认情况下,当使用foreach迭代时,该表达式将执行。当您迭代foreach时,会执行IEnumerablelinq语句,但您可以强制它使用.ToList()更快地迭代。

    我的意思是:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    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 ) ...


    上述关键差一人,ironically回答对一个问题作为一个duplicated封闭它。

    IEnumerable is read-only and List is not.

    在实际的战略和IEnumerable类型之间的差异


    要认识到的最重要的一点是,使用LINQ不会立即对查询进行评估。它只作为迭代生成的IEnumerableforeach中的一部分运行——这就是所有奇怪的代表所做的。

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

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


    IEnumerable的优点是延迟执行(通常使用数据库)。在实际循环访问数据之前,不会执行查询。这是一个等待到需要时的查询(又称懒惰加载)。

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

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


    在将一misused概念股在下跌到那一天。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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() );

    预期结果

    1
    2
    3
    // I was expecting    
    print( startingWith_M.ToList() ); // mercedes, mazda
    print( startingWith_F.ToList() ); // fiat, ferrari

    实际结果

    1
    2
    3
    // what printed actualy  
    print( startingWith_M.ToList() ); // mazda
    print( startingWith_F.ToList() ); // ford, fiat, ferrari

    解释

    根据对方的回答,评价结果是deferred ToList直到电话或类似的方法ToArrayinvocation实例。

    所以我重写的代码在本案例为:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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播放

    http:/ / / / 0 repl.it e8ki


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

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


    在添加发布以上所有的答案,这里是我的两美分。有许多其他的比其他类型的列表,这样ICollection implements IEnumerable,ArrayList等。所以,如果我们有任何IEnumerable作为参数的方法,我们可以通过任何类型的采集功能。即,我们可以有两种操作方法对abstraction没有任何具体实施。


    有很多的案例(如一个无限狡猾或狡诈的甚大)在两个IEnumerable不能转变的战略。最明显的例子是全素数,全是用他们的Facebook用户的资料,或所有的项目在eBay上。

    "不同的是,"狡猾"的对象是"对仓库在这里和现在"尽头",IEnumerable对象的"工作"(只是一小时"。所以,如果我想通过所有的项目在eBay上,一个一个小时会是什么的一点小的计算机可以处理,但.tolist()"将康医药跑了我的记忆,不管是大我的电脑技能。没有电脑我市和行动本身包含这样一种巨大的金额数据。