C# 为什么在IEnumerables上使用标准扩展方法

C# 为什么在IEnumerables上使用标准扩展方法,c#,visual-studio,linq,extension-methods,C#,Visual Studio,Linq,Extension Methods,当我在列表上使用标准扩展方法时,例如 其中(…) 结果总是IEnumerable,并且 您决定执行列表操作,例如Foreach() 我们需要Cast(不漂亮)或使用ToList()扩展方法 (可能)使用消耗更多内存的新列表(是这样吗?) 或 如果要在列表上创建一个简单的foreach,可以执行以下操作: foreach (var item in myList.Where([Where clause])) { // Do something with each item. } 如果要在列

当我在列表上使用标准扩展方法时,例如 其中(…)

结果总是IEnumerable,并且 您决定执行列表操作,例如Foreach()

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

(可能)使用消耗更多内存的新列表(是这样吗?)


如果要在列表上创建一个简单的foreach,可以执行以下操作:

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

如果要在列表上创建一个简单的foreach,可以执行以下操作:

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

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
中有100000个长度超过5的项目,这可能会很麻烦

LINQ返回
IEnumerable
的原因是它(LINQ到对象)被设计为可组合和流式处理,如果您转到列表,这是可能的。例如,几个
的组合,其中
/
选择
等应该严格地需要创建大量中间列表(实际上,LINQ不需要)

当你认为不是所有的序列都有界时,这更重要;有无限个序列,例如:

static IEnumerable<int> GetForever() {
    while(true) yield return 42;
}
var thisWorks = GetForever().Take(10).ToList();
静态IEnumerable GetForever(){
而(真实)收益率为42;
}
var thisWorks=GetForever().Take(10.ToList();

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

作为
绝对不是一个好方法,如果它能工作,我会感到惊讶

关于什么是“最好的”,我建议用
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
中有100000个长度超过5的项目,这可能会很麻烦

LINQ返回
IEnumerable
的原因是它(LINQ到对象)被设计为可组合和流式处理,如果您转到列表,这是可能的。例如,几个
的组合,其中
/
选择
等应该严格地需要创建大量中间列表(实际上,LINQ不需要)

当你认为不是所有的序列都有界时,这更重要;有无限个序列,例如:

static IEnumerable<int> GetForever() {
    while(true) yield return 42;
}
var thisWorks = GetForever().Take(10).ToList();
静态IEnumerable GetForever(){
而(真实)收益率为42;
}
var thisWorks=GetForever().Take(10.ToList();
直到
ToList
它正在组成迭代器,才生成中间列表。不过,有一些缓冲操作,如
OrderBy
,需要先读取所有数据。大多数LINQ操作都是流操作。

您不能将(
转换为
IEnumerable
转换为
列表<代码>IEnumerable
在访问项目时对其求值。调用
ToList()
将枚举集合中的所有项并返回一个新列表,这会导致内存效率低下,而且是不必要的。如果您愿意对任何集合使用
ForEach
扩展方法,那么最好编写一个新的
ForEach
扩展方法,用于任何集合

public static void ForEach<T>(this IEnumerable<T> enumerableList, Action<T> action)
{
    foreach(T item in enumerableList)
    {
        action(item);
    }
}
公共静态void ForEach(此IEnumerable enumerableList,Action)
{
foreach(枚举列表中的T项)
{
行动(项目);
}
}
您不能将(
转换为
IEnumerable
转换为
列表
<代码>IEnumerable
在访问项目时对其求值。调用
ToList()
将枚举集合中的所有项并返回一个新列表,这会导致内存效率低下,而且是不必要的。如果您愿意对任何集合使用
ForEach
扩展方法,那么最好编写一个新的
ForEach
扩展方法,用于任何集合

public static void ForEach<T>(this IEnumerable<T> enumerableList, Action<T> action)
{
    foreach(T item in enumerableList)
    {
        action(item);
    }
}
公共静态void ForEach(此IEnumerable enumerableList,Action)
{
foreach(枚举列表中的T项)
{
行动(项目);
}
}
其中一个方法是允许对任何受支持的数据类型进行组合查询,这是通过使用通用接口而不是具体类(如您提到的
IEnumerable
)指定返回类型来实现的。这允许根据需要将螺母和螺栓实现为具体类(例如,
whereEnumerableInterator
或提升到SQL查询中),或使用方便的
yield
关键字

此外,还有一个问题。基本上,在实际使用查询之前,没有真正的工作完成。这使得可能非常昂贵的()操作只能完全根据需要完成

如果
List.Where
返回另一个
List
,则可能会限制合成,并肯定会阻碍延迟执行(更不用说生成多余内存)

因此,回顾一下您的示例,使用
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));
//假设myList有20000个条目
//如果.Where返回一个新列表,我们的内存可能会翻倍!
var largeStrings=myList.Where(ss=>ss.Length>100);
foreach(最大字符串中的var项目)