【问题标题】:Why Standard Extension Method on IEnumerables为什么在 IEnumerables 上使用标准扩展方法
【发布时间】:2011-12-30 04:49:14
【问题描述】:

当我在 List 上使用标准 Extension Method 时,例如 在哪里(...)

结果总是 IEnumerable,当 您决定进行列表操作,例如 Foreach()

我们需要 Cast(不漂亮)或使用 ToList() 扩展方法

(也许)使用了一个消耗更多内存的新列表(对吗?):

List<string> myList=new List<string>(){//some data};

编辑:这个演员表不起作用)

myList.Where(p=>p.Length>5).Tolist().Foreach(...);

(myList.Where(p=>p.Length>5) as List<string>).Foreach(...);

哪种代码更好还是有第三种方式?

编辑: Foreach 是一个示例,将其替换为 BinarySerach

myList.Where(p=>p.Length>5).Tolist().Binarysearch(...)

【问题讨论】:

  • 你想在Foreach做什么?
  • Foreach 是您假设 BinarySearch() 的列表示例方法
  • 正如sixlettervariables 指出的那样,您应该看看您想用这个实现什么。我猜,你可以做 foreach 所做的事情,就在查询中,可能在 select 语句中。
  • 在我看来,ForEach 方法是一个糟糕的设计决策,不应使用。它搞砸了可读性,使您的代码意图变得不那么明显。当您真正尝试在列表上进行竞标时,您实际上是在告诉列表进行竞标。它与 Linq 的效果如此糟糕的事实是为什么两者不应该混合使用的一个完美例子(在我看来,.ForEach 根本不应该使用)
  • 这里的主要问题是你混合了两个概念; LINQ (System.Enumerable) 方法和 List&lt;T&gt; 方法,并期望它们可以互换 - 但很简单:这两个想法之间存在根本差异。

标签: c# visual-studio linq extension-methods


【解决方案1】:

as 绝对不是一个好方法,如果它有效,我会感到惊讶。

就什么是“最好”而言,我建议foreach 而不是ForEach

foreach(var item in myList.Where(p=>p.Length>5)) {
    ... // do something with item
}

如果您迫切地想使用列表方法,也许:

myList.FindAll(p=>p.Length>5).ForEach(...);

确实

var result = myList.FindAll(p=>p.Length>5).BinarySearch(...);

但请注意,这确实(与第一个不同)需要额外的数据副本,如果 myList 中有 100,000 个长度超过 5 的项目,这可能会很麻烦。

LINQ 返回IEnumerable&lt;T&gt; 的原因是它(LINQ-to-Objects)被设计为可组合和流式传输,如果您转到列表,这是不可能。例如,几个where / select 等的组合应该严格需要创建大量中间列表(事实上,LINQ 不需要)。

当您考虑到并非所有序列都是有界的时,这一点就更加重要了。有无限序列,例如:

static IEnumerable<int> GetForever() {
    while(true) yield return 42;
}
var thisWorks = GetForever().Take(10).ToList();

作为 直到 ToList 它正在组成迭代器,生成中间列表。不过,有一些缓冲操作,例如OrderBy,需要先读取所有数据。大多数 LINQ 操作都是流式的。

【讨论】:

    【解决方案2】:

    design goals for LINQ 之一是允许对任何受支持的数据类型进行可组合查询,这是通过使用通用接口而不是具体类指定返回类型来实现的(例如 IEnumerable&lt;T&gt; 为你注意到了)。这允许根据需要实现具体细节,或者作为具体类(例如 WhereEnumerableIterator&lt;T&gt; 或提升到 SQL 查询中)或使用方便的 yield 关键字。

    另外,另一个design philosophy of LINQ is one of deferred execution。基本上,在您真正使用该查询之前,还没有完成任何实际工作。这允许仅根据需要完成可能昂贵的 (or infinite as Mark notes) 操作。

    如果List&lt;T&gt;.Where 返回另一个List&lt;T&gt;,它可能会限制组合并且肯定会阻碍延迟执行(更不用说产生过多的内存)。

    因此,回顾您的示例,使用 Where 运算符结果的最佳方式取决于您想用它做什么!

    // This assumes myList has 20,000 entries
    // if .Where returned a new list we'd potentially double our memory!
    var largeStrings = myList.Where(ss => ss.Length > 100);
    foreach (var item in largeStrings)
    {
        someContainer.Add(item);
    }
    
    // or if we supported an IEnumerable<T>
    someContainer.AddRange(myList.Where(ss => ss.Length > 100));
    

    【讨论】:

    • 很高兴提到延迟执行!
    【解决方案3】:

    如果你想在一个列表上做一个简单的 foreach,你可以这样做:

    foreach (var item in myList.Where([Where clause]))
    {
        // Do something with each item.
    }
    

    【讨论】:

    • .Foreach() 是一个例子,你假设 BinarySearch() 而不是 Foreach()
    【解决方案4】:

    您不能将 (as) IEnumerable&lt;string&gt; 转换为 List&lt;string&gt;IEnumerable 在您访问项目时评估这些项目。调用ToList&lt;string&gt;() 将枚举集合中的所有项目并返回一个新的List,这有点内存效率低下,也是不必要的。如果您愿意对任何集合使用 ForEach 扩展方法,最好编写一个适用于任何集合的新 ForEach 扩展方法。

    public static void ForEach<T>(this IEnumerable<T> enumerableList, Action<T> action)
    {
        foreach(T item in enumerableList)
        {
            action(item);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2015-08-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-02
      • 2015-09-08
      • 2013-08-08
      • 2010-09-29
      相关资源
      最近更新 更多