为何IEnumerables上的标准扩展方法

Rez*_*eni 1 c# linq extension-methods visual-studio

当我在列表上使用标准的扩展方法,如 Where(...)

结果总是IEnumerable,当你决定做一个列表操作,如Foreach()

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

(也许)使用一个消耗更多内存的新List(是吗?):

List<string> myList=new List<string>(){//some data};
Run Code Online (Sandbox Code Playgroud)

(编辑:这个演员不工作)

myList.Where(p=>p.Length>5).Tolist().Foreach(...);
Run Code Online (Sandbox Code Playgroud)

要么

(myList.Where(p=>p.Length>5) as List<string>).Foreach(...);
Run Code Online (Sandbox Code Playgroud)

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

编辑: Foreach是一个示例,用BinarySerach替换它

myList.Where(p=>p.Length>5).Tolist().Binarysearch(...)
Run Code Online (Sandbox Code Playgroud)

Mar*_*ell 9

as绝对不是一个好办法,我会感到惊讶,如果它的工作原理.

就什么是"最佳"而言,我建议foreach而不是ForEach:

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

如果您迫切想要使用list方法,可能:

myList.FindAll(p=>p.Length>5).ForEach(...);
Run Code Online (Sandbox Code Playgroud)

或者确实

var result = myList.FindAll(p=>p.Length>5).BinarySearch(...);
Run Code Online (Sandbox Code Playgroud)

但请注意这确实(与第一)要求的数据,这可能是一个痛苦的额外副本,如果有100,000个项目myList有长度超过5.

该LINQ返回的原因IEnumerable<T>是,这个(LINQ到对象)被设计为可组合和流媒体,是不是,如果你去到一个列表可能的.例如,一些where/ selectetc 的组合应该严格地需要创建大量的中间列表(实际上,LINQ没有).

当你考虑到并非所有序列都是有界的时候,这一点就更为重要; 有无限的序列,例如:

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

作为直至ToList是构成的迭代器,生成中间列表.但是,有一些缓冲操作OrderBy需要首先读取所有数据.大多数LINQ操作都是流式传输.


use*_*116 5

LINQ设计目标之一是允许对任何支持的数据类型进行可组合查询,这可以通过使用通用接口而不是具体类(如IEnumerable<T>您所注意到的)指定返回类型来实现.这允许根据需要实现螺母和螺栓,作为具体类(例如,WhereEnumerableIterator<T>或提升到SQL查询中)或使用方便的yield关键字.

此外,LINQ的另一种设计理念是延迟执行.基本上,在您实际使用查询之前,尚未完成任何实际工作.这允许仅在需要时完成可能昂贵的(或Mark注释的无限)操作.

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

所以,回顾一下你的例子,使用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));
Run Code Online (Sandbox Code Playgroud)