[英]List<T>.ForEach vs. custom IEnumerable<T> extension
说我有一节课:
public class MyClass
{
...
}
和一个返回IEnumerable<MyClass>
的webservice方法
Web服务的使用者定义了一些方法:
public void DoSomething(MyClass myClass)
{
...
}
现在,消费者可以通过两种方式在Web服务方法的结果上调用DoSomething
:
var result = // web service call
foreach(var myClass in result)
{
DoSomething(myClass);
}
要么:
var result = // web service call
result.ToList().ForEach(DoSomething);
毋庸置疑,我更喜欢第二种方式,因为它更短,更具表现力(一旦你习惯了我的语法)。
现在,Web服务方法只公开IEnumerable<MyClass>
,但它实际上返回一个List<MyClass>
,其中(AFAIK)表示实际的序列化对象仍然是List<T>
。 但是,我发现(使用反射器)Linq方法ToList()
生成IEnumerable<T>
中所有对象的副本,而不管实际的运行时类型(在我看来,它可能只是将参数转换为List<T>
如果它已经是一个)。
这显然有一些性能开销,特别是对于大型列表(或大型对象列表)。
那么我该怎么做才能克服这个问题,为什么Linq中没有ForEach
方法呢?
顺便说一句,他的问题与这个问题含糊不清。
您可以编写扩展方法,但有充分的理由说明为什么ForEach没有在IEnumerable<T>
。 第二个例子
result.ToList().ForEach(DoSomething);
将IEnumerable复制到List中(除非它已经是List,我假设)所以你最好只用旧的foreach(var r in result) {}
迭代IEnumerable foreach(var r in result) {}
。
附录:
对我来说,Eric Lippert的文章的关键点是添加ForEach没有任何好处,并增加了一些潜在的陷阱:
第二个原因是,这样做会增加语言的新代表性能力。 这样做可以让你重写这个完全清晰的代码:
foreach(foo foo in foos){声明涉及foo; }
进入这段代码:
foos.ForEach((Foo foo)=> {声明涉及foo;});
它使用几乎完全相同的字符,顺序略有不同。 然而第二个版本更难理解,更难调试,并引入了闭包语义,从而可能以微妙的方式改变对象的生命周期。
我更喜欢这个: -
foreach (var item in result.ToList())
{
DoSomething(item);
}
它是一个更清晰的习语,它说收集一起列表的东西, 然后做一些重要的事情,可能会改变应用程序的状态。 它的旧学校,但它的工作,实际上更广泛的观众可以理解。
我用2种方法。 一个迭代列表,一个使用懒惰的eval。 我根据情况定义使用它们。
public static IEnumerable<T> ForEachChained<T>(this IEnumerable<T> source, Action<T> action)
{
foreach (var item in source)
{
action(item);
yield return item;
}
}
public static IEnumerable<T> ForEachImmediate<T>(this IEnumerable<T> source, Action<T> action)
{
foreach (var item in source)
{
action(item);
}
return source;
}
您可以为IEnumerable<T>
编写自己的扩展方法,如下所示:
public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
{
foreach (T t in enumerable)
action(t);
}
Linq中不存在这样的方法,因为Linq基本上用于查询,而不是简单的迭代。
另请注意,使用实际的List<T>
实例时,将不会调用扩展方法,因为实例方法在共享签名时优先于扩展方法。
例如,以下代码不会调用扩展方法:
var l = new List<MyClass>();
l.Add(new MyClass());
l.ForEach(DoSomething);
以下是:
IEnumerable<MyClass> l = new List<MyClass>(new []{new MyClass()});
l.ForEach(DoSomething);
您可以编写自己的扩展方法ToList(此List theList){return theList;}然后避免开销。 由于您的扩展方法是最具体的方法,因此将调用它,而不是IEnumerable上的方法
如果您决定通过扩展方法执行此操作,我将其称为ForAll而不是ForEach。 这是为了使用与.NET 4.0中的Parallel Extensions相同的语法使用:
var result = // web service call
result.AsParallel().ForAll(DoSomething);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.