什么是迭代器的“进一步过滤”?

时间:2014-08-14 07:54:13

标签: c# filtering ienumerable ilist

我总是更喜欢使用 IEnumerable 而不是 List ,原因显而易见,如果适用的话。在当前的项目中,我遇到了 IList ,在我发现它之后,互联网告诉我,除了单个属性之外,它们之间没有显着差异 - 支持进一步过滤

由于我不确定C#中的迭代器是什么意思,我也是这样。任何可能的相关答案都会在“支持进一步过滤”的大量点击中淹没,告诉我 IEnumerable 会这样做,而 IList 则没有。

所以我在这里问两个问题。

  1. 对进一步过滤的支持是什么意思?
  2. 我如何谷歌这样的术语(在更一般意义上)?
  3. 由于这是基于很多帖子的一般观察,我不能在这里列出所有这些。一个例子可能是this particular link

1 个答案:

答案 0 :(得分:8)

没有“进一步过滤”这样的事情。

过滤集合通常使用IEnumerable.Where扩展方法完成,该方法是为IEnumerable接口定义的。由于IList继承自IEnumerable,因此您可以在两个接口上调用Where(在Where上调用IList实际调用IEnumerable.Where扩展方法)。因此,在这两种情况下,都会调用相同的基本方法,并且结果值的类型将是IEnumerable(应用于列表时不是IList)。这可能是混淆的根源(“你不能再过滤IList因为你不再拥有它了吗?”),但没有什么可以阻止你再次过滤生成的IEnumerable<T>,甚至写你的自己的扩展方法,可以在每次调用时创建一个新的List

在问题中链接的帖子质量低,不应该被信任。

详细说明见下文。

您可以过滤两个接口中的元素几乎相同,但在这两种情况下通常都会使用IEnumerable扩展方法(即LINQ),因为IList继承自IEnumerable。在这两种情况下,您可以根据需要链接尽可能多的Where语句:

// `items` is an `IEnumerable<T>`, so we can call the `Where` extension method.
// Each call creates a new instance, and keeps the previous one unmodified.
IEnumerable<T> items = GetEnumerableItems();
var filteredItems = items
    .Where(i => i.Name == "Jane")      // returns a new IEnumerable<T>
    .Where(i => i.Gender == "Female")  // returns a new IEnumerable<T>
    .Where(i => i.Age == 30)           // returns a new IEnumerable<T>

// `list` is an `IList<T>`, which also inherits from `IEnumerable<T>`.
// Calling `Where` on a list will also not modify the original list.
IList<T> list = GetEnumerableItems();
var filteredList = list
    .Where(i => i.Name == "John")      // returns a new IEnumerable<T>
    .Where(i => i.Gender == "Male")    // returns a new IEnumerable<T>
    .Where(i => i.Age == 30)           // returns a new IEnumerable<T>
    .ToList();                         // returns a new List<T> (optional)

谷歌搜索该术语会返回几篇提到它的文章(如thisthis),它们似乎都复制了相同的来源,看起来就像抄袭而没有背后的实际推理。我唯一能想到的是,将Where应用于IEnumerable<T>会返回一个新的(已过滤的)IEnumerable<T>,您可以再次应用Where(过滤它) “进一步”)。但这真的很模糊,因为将Where应用于IList<T>不会阻止您对其进行过滤,即使生成的界面是IEnumerable<T>。正如评论中所提到的,值得一提的是List<T>类作为IList<T>的具体实现,公开了一个FindAll方法,它返回一个新的过滤后的具体List<T>(并且可以“进一步过滤”),但这不是IList<T>的一部分。

重复过滤IEnumerable<T>并将列表过滤到新列表(例如使用FindAll)之间的主要区别在于后者需要在每个列表中创建一个新的List<T>实例step,虽然IEnumerable<T>使用延迟执行,除了为每个Where调用存储一些微小的状态信息之外,不会占用额外的内存。同样,为了避免混淆,如果您在Where上致电List<T>,您仍然可以获得IEnumerable<T>懒惰的好处。

实际差异:

IList (或实际上IList<T>,我假设您指的是)表示可以通过索引单独访问的对象集合< / strong>即可。这意味着您可以有效地(在O(1)时间内)获取某个位置的对象值以及列表的长度。 “坏事”是(假设它在引擎盖下实现为List<T>),这意味着你需要将整个集合保留在内存中。

“{strong> IEnumerable (即其通用对应IEnumerable<T>)可以做的是迭代(零个或多个)项目。它没有索引的概念(你不能“跳转”到索引,而不实际迭代或跳过该项之前的所有项目)。而且你也无法在一般情况下有效地获得长度,而不是每次都实际计算项目。另一方面,IEnumerable是惰性求值的,这意味着它的元素在它们即将被评估之前不必存在于内存中。它可以包装数据库表,下面有数十亿行,在迭代时从磁盘中提取。它甚至可以是一个无限的集合。