何时使用List <t>,IEnumerable <t>和ArrayList </t> </t>

时间:2012-03-25 19:56:35

标签: c# asp.net list ienumerable

我的问题非常简单。我什么时候应该使用List,IEnumerable和ArrayList。

这是我的情景。我正在使用LINQ在Web应用程序中工作。信息以IEnumerable:

的形式返回
IEnumerable<Inventory> result = from Inventory i in db where.... 

我不确定IEnumerable是如何工作的,但是每个操作都需要花费很多时间来执行。更具体地说,result.Count(),result.ElementAt(i),result.ToList等,每个操作都需要相当长的时间。

所以,我想知道我是否应该通过执行result.ToList将其视为List,而不是使用IEnumerable变量。

谢谢!

7 个答案:

答案 0 :(得分:6)

如果我理解你正在做什么,你会有from Inventory i in db select i这样的查询,然后对结果做几个操作:

var count = result.Count();
var fifth = result.ElementAt(5);
var allItems = result.ToList();

现在考虑将查询作为不同类型时会发生什么:

  • IQueryable<T>

    var result = from Inventory i in db select i;
    IQueryable<Inventory> result = from Inventory i in db select i;
    

    以上两行是相同的。它们实际上并没有进入数据库,只是创建了查询的表示。如果你有这个,Count()将执行像SELECT COUNT(*) FROM Inventory这样的SQL查询,ElementAt(5)将执行另一个只接受表中第五项的查询,而ToList()将执行类似SELECT * FROM Inventory,但这就是我们想要的。

  • IEnumerable<T>

    IEnumerable<Inventory> result = from Inventory i in db select i;
    

    再次执行此操作不会进入数据库,它只会创建查询的表示形式。但它是一种不能使用特定于IQueryable<T>的方法的表示,因此任何LINQ操作都将枚举该集合,该集合将执行SELECT * FROM Inventory之类的SQL查询。

    因此,对于示例:Count()将仅执行SELECT * …查询以计算结果中的项目。 ElementAt(5)将再次执行整个查询 ,只会丢弃除第五个以外的所有项目。 ToList()将再次执行查询

  • List<T>

    List<Inventory> result = (from Inventory i in db select i).ToList();
    

    这实际上会立即执行SELECT * FROM Inventory查询,一次。您使用result执行的所有操作都不会触及数据库,它们将在内存中完成。

你应该从中拿走什么?首先,从不使用IEnumerable<T>作为数据库查询的类型。它表现糟糕。

如果您想对结果进行多项不同的操作,使用IQueryable<T>可能是最佳解决方案。

如果您想要检索整个结果,请尽快使用ToList()(或ToArray()),然后使用生成的List<T>

答案 1 :(得分:4)

永远不要使用ArrayList。保留ArrayList以与pre -.NET 2.0兼容。它相当于List<object>,并且没有理由不在任何正常情况下使用泛型类型。

从您的代码示例中可以看出,您正在使用LINQ to SQL或类似的框架从数据库中获取数据。在这种情况下,select语句本身不会带来数据,它只是构造查询。当你调用像Count()或ToList()这样的方法时,它会获取数据 - 这就是它看起来很慢的原因。它并不慢,只是在行动中的延迟加载。

使用IEnumerable的优点是您不必一次加载所有数据。如果您只是使用特定的where子句进行查询,或者调用Take(1)来获取第一个元素,那么LINQ提供程序应该足够智能,只能从数据库中获取必要的元素。但是如果你调用Count()或ToList(),它必须检索整个数据集。如果您发现自己需要这类信息,您可能需要致电ToListToArray并在内存列表中完成剩余的工作,这样您就不必点击了DB再次。

答案 2 :(得分:2)

只有在调用ToList()或其他类似方法时才会执行查询。

这称为Deffered Execution

只要result可以使用IEnumerable。执行的性能LINQ不依赖于你对result的使用,因为最终它被视为IEnumerable。

但LINQ性能取决于基础数据。

[WAS EDITED WITH DETAILS]

答案 3 :(得分:1)

使用IEnumerable或IList之间的区别实际上非常简单(表面上)。

您应该查看两个接口定义的合同。 IEnumerable只允许您枚举序列。换句话说,访问数据的唯一方法是使用枚举器,通常在foreach循环中。所以count函数的简单实现就像:

public static int Count(this IEnumerable<T> source) {
    int count = 0;
    foreach(var item in myEnumerable)
    {
        count++;
    }
    return count;
}

这意味着计算可枚举项目数量所需的时间将随项目数量线性增加。此外,因为内部没有以任何方式存储,所以每次想要计数时都必须执行此循环。

IList已经公开了Count属性。这是合同的一部分。要实现Count(),你只需要调用Count属性。无论项目数量如何,这都将花费相同的时间。

考虑这一点的一个简单方法是(特别是使用Linq)将IEnumerable视为您需要的项目的规范。只要您不访问数据,几乎不需要任何时间来构建。一旦你开始枚举(任何返回基本上不是IEnumerable的东西),代码就会执行,可能需要一些时间。

至于你的上下文,我通常喜欢做的是将Linq执行保留在控制器中。所以我进行构建,然后在将其发送到视图之前ToList或ToArray。原因很简单:如果我必须做的只是简单地访问视图中的数据,这意味着我在我看来做的太多了。我现在被迫将这个逻辑移动到我的控制器动作中,使我的视图尽可能干净。

答案 4 :(得分:0)

如果对Linq查询提供程序使用linq表达式,则结果将是IQueryable<T>,这是IEnumerable<T>的扩展名。

每次迭代IQueryable<T>时,Linq查询提供程序将对基础数据源执行查询。因此,如果您希望多次迭代结果,则首先将其转换为列表会更有效(.ToList())。

请注意,将结果转换为列表时,应使用List<T>的实际成员,而不是IEnumerable<T>的扩展方法。例如,list.ElementAt(i)list.Count()都在O(n)时间内执行,而list[i]list.Count则会在固定时间内执行。

答案 5 :(得分:0)

尽可能使用通用列表/ IEnumerable。

避免使用ArrayList。这可能导致值类型的装箱和参考类型的转换。 IEnumerable是相同的 - 除非你处理对象,否则最好避免使用。

IEnumerable<T>表现出非常好的协方差,逆变特征。然而它显示delayed execution这是一个诅咒和祝福。

在将接口公开为List<T>时,

IEnumerable<T>更适合内部使用。 List<T>不支持逆变。

答案 6 :(得分:0)

使用的答案是“它取决于,但主要是使用List”。

根据问题的全部内容(长时间延迟运行.Count()和其他方法),您应首先对查询结果执行toList(),然后将其用于任何进一步的访问。

这就是原因。 IEnumerable几乎是一个查询。由于被查询的数据可以在查询的运行之间发生变化,因此对该IEnumerable的单个方法调用会导致另一个数据库查找。

因此,每次调用.Count()时,都必须访问数据库并获取与查询匹配的所有对象的计数。每次执行elementAt(x)时,即使x没有改变,有人仍然需要通过数据库并获取其中的任何内容,因为IEnumerable不能假设数据没有改变。

另一方面,如果您使用List获得了查询的快照,那么获取Count或访问随机元素的速度非常快。

那么,使用哪种 - 取决于它。如果每次访问IEnumerable时,您需要知道数据库(或任何数据源)中的内容,那么您必须使用IEnumerable。如果您只关心执行初始查询时的内容或需要对一致(和/或静态)数据源执行操作,请使用List。你第一次访问时仍然会花些时间,但其他一切都会很快。