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

本文关键字:迭代器 进一步过滤 过滤 进一步 什么 | 更新日期: 2023-09-27 18:18:01

我总是喜欢使用IEnumerable而不是List,原因很明显,在适用的情况下。在当前的项目中,我偶然遇到了IList,在我使用它之后,Internet告诉我它们之间除了一个属性之外没有明显的区别—支持进一步过滤。

因为我不确定这对于c#中的迭代器意味着什么,所以我也这么做了。任何可能的相关答案都淹没在"支持进一步过滤"的无数点击中,告诉我IEnumerable可以,而IList不能。

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

  1. 支持进一步过滤意味着什么?
  2. 我如何在谷歌上搜索这些术语(在更一般的意义上)?

由于这是基于许多帖子的一般观察,我无法在这里列出它们。下面这个链接就是一个例子:

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

没有"进一步过滤"这回事。

过滤集合通常使用IEnumerable.Where扩展方法完成,该方法是为IEnumerable接口定义的。由于IList继承自IEnumerable,您可以在两个接口上调用Where(在IList上调用Where实际上调用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)

在谷歌上搜索这个词,会得到几篇提到它的文章(比如这个,或者这个),它们似乎都是复制了相同的来源,似乎是没有实际理由的抄袭。我想到的唯一一件事是,将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>实例,而IEnumerable<T>使用延迟执行,除了为每个Where调用存储一些微小的状态信息外,不占用额外的内存。同样,为了避免混淆,如果在List<T>上调用Where,您仍然可以获得IEnumerable<T>惰性的好处。

实际差异:

IList (或者实际上是IList<T>,我假设你指的是)表示可以通过索引单独访问的对象集合。这意味着您可以有效地(在O(1)时间内)获得某个位置上对象的值,以及列表的长度。"不好的事情"是(假设它是作为List<T>在底层实现的),这意味着您需要将整个集合保存在内存中。

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