IEnumerable到List

时间:2014-05-08 15:41:15

标签: c# linq ienumerable generic-list

为什么IEnumerable.ToList()在以下情况下不起作用:

 var _listReleases= new List<string>;
_listReleases.Add("C#")
_listReleases.Add("Javascript");
_listReleases.Add("Python");

 IEnumerable sortedItems = _listReleases.OrderBy(x => x);
_listReleases.Clear();
_listReleases.AddRange(sortedItems); // won't work
_listReleases.AddRange(sortedItems.ToList()); // won't work

注意:_listRelealse将为null

2 个答案:

答案 0 :(得分:2)

LINQ被懒惰地评估了。当你运行这一行时:

IEnumerable sortedItems = _listReleases.OrderBy(x => x);

你实际上并没有正确地订购物品。相反,您正在构建一个枚举,当枚举时,它将按顺序返回_listReleases中当前的对象。因此,当您Clear()列表时,它不再有任何要订购的商品。

您需要强制它在之前评估,以清除_listReleases。一种简单的方法是添加ToList()电话。此外,与AddRange不兼容的IEnumerable类型也不会接受它。您可以使用var隐式地将其键入List<string>,这将起作用,因为List<T> : IEnumerable<T>(它实现了接口)。

var sortedItems = _listReleases.OrderBy(x => x).ToList();
_listReleases.Clear();
_listReleases.AddRange(sortedItems);

您还应该注意,ToList()等方法是IEnumerable<T>的扩展方法,而不是IEnumerable,因此((IEnumerable)something).ToList()不起作用。与Java不同,Something<T>Something在C#中是完全不同的类型。

答案 1 :(得分:2)

由于这一行不起作用:

_listReleases.Clear();

首先,_listReleases此时为null。它只是,这是完全不同的事情。

但要解释为什么这不能按预期工作:IEnumerable接口类型实际上为任何事物分配或保留存储空间。它表示可以与foreach循环一起使用的对象,仅此而已。它实际上不需要将项目存储在集合中。

有时,IEnumerable引用确实将这些项目放在同一个对象中,但它不必。这就是这里发生的事情。 OrderBy()扩展方法仅创建一个对象,该对象知道如何查看原始列表并按特定顺序返回项目。但是这些物品没有存储空间。它仍然取决于它的原始数据源。

此情况的最佳解决方案是此时停止使用_listReleases变量,而只使用sortedItems变量。只要前者不是收集的,后者就会做你需要的。但是如果你真的想要_listReleases变量,你可以这样做:

_listReleases = sortedItems.ToList();

现在回到IEnumerables。这个属性有一些很好的好处,不需要立即存储项目本身,只是抽象了迭代集合的能力:

  • 懒惰评估 - 生产这些项目所需的工作在被要求之前不会完成(通常,这意味着它不需要全部完成,大大提高了性能)。
  • 组合 - 可以在程序期间修改IEnumerable对象,以将新的规则集合并到文件结果中。通过允许您将一组复杂的排序或过滤需求分解为其组件部分,可以降低程序复杂性并提高可维护性。这也使得构建程序变得更加容易,用户可以在运行时轻松地确定这些规则,而不是程序员在编译时提前确定。
  • 内存效率 - IEnumerable可以从数据库等来源迭代数据集合,只需要在任何给定时间将当前记录保存到内存中。此功能还可用于创建无界集合:可以延伸到无穷大的项目集。如果要求,您可以使用BigInteger类型构建IEnumerable以计算下一个无穷大的素数。此外,您可以通过将此组合与可组合性功能相结合,以有用的方式使用该集合而不会崩溃或挂起程序,以便程序知道何时停止。