List.ForEach 与自定义 IEnumerable扩大
假设我有一个类:
public class MyClass
{
...
}
和一个返回 IEnumerable
的 Web 服务方法
Web 服务的使用者定义了一些方法:
public void DoSomething(MyClass myClass)
{
...
}
现在,使用者可以对结果调用 DoSomething
webservice 方法有两种方式:
var result = // web service call
foreach(var myClass in result)
{
DoSomething(myClass);
}
或:
var result = // web service call
result.ToList().ForEach(DoSomething);
不用说,我更喜欢第二种方式,因为它更短且更具表现力(一旦您习惯了我所拥有的语法)。
现在,Web 服务方法仅公开一个 IEnumerable
,但它实际上返回一个 List
,这(AFAIK)意味着实际的序列化对象仍然是一个列表
。但是,我发现(使用反射器)Linq 方法 ToList()
会复制 IEnumerable
中的所有对象,无论实际运行时类型如何(在我看来,它可能只是将参数转换为 List
(如果它已经是一个)。
这显然会带来一些性能开销,特别是对于大型列表(或大型对象列表)。
那么我该怎么做才能克服这个问题,为什么 Linq 中没有 ForEach
方法呢?
顺便说一句,他的问题与这个问题隐约相关。
Say I have a class:
public class MyClass
{
...
}
and a webservice method that returns an IEnumerable<MyClass>
The consumer of the webservice defines some method:
public void DoSomething(MyClass myClass)
{
...
}
Now, the consumer can call DoSomething
on the result of the webservice method in two ways:
var result = // web service call
foreach(var myClass in result)
{
DoSomething(myClass);
}
or:
var result = // web service call
result.ToList().ForEach(DoSomething);
Needless to say I much prefer the second way since it is much shorter and more expressive (once you get used to the syntax, which I have).
Now, the web service method only exposes an IEnumerable<MyClass>
, but it actually returns a List<MyClass>
which (AFAIK) means that the actual serialized object is still a List<T>
. However, I have found (using reflector) that the Linq method ToList()
makes a copy of all the objects in the IEnumerable<T>
regardless of the actual runtime type (in my opinion, it could just have casted the argument to a List<T>
if it already was one).
This obviously has some performance overhead, especially for large list (or lists of large objects).
So what can I do to overcome this problem, and why is there no ForEach
method in Linq?
By the way, his question is vaguely related to this one.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
您可以编写扩展方法,但有好的ForEach 未在
IEnumerable
上实现的原因。第二个示例将 IEnumerable 复制到列表中(我假设它已经是一个列表),因此您最好使用旧的
foreach(var r in result) {}
迭代 IEnumerable。附录:
对我来说,Eric Lippert 文章的要点是添加 ForEach 没有任何好处,而且增加了一些潜在的陷阱:
You can write an extension method but there are good reasons why ForEach is not implemented on
IEnumerable<T>
. The second examplecopies the IEnumerable into a List (unless it's already a List, I assume) so you're better off just iterating the IEnumerable with good old
foreach(var r in result) {}
.Addendum:
For me, the key point of Eric Lippert's article is that adding ForEach has no benefit and adds some potential pitfalls:
我更喜欢这个:-
这是一个更清晰的习惯用法,它表示将一系列内容收集在一起然后做一些可能改变应用程序状态的重要事情。这是一种老派的做法,但它很有效,而且实际上更容易为更广泛的受众所理解。
I would prefer this:-
Its a much clearer idiom, it says collect a list of stuff together then do something important that may change the state of the application. Its old school but it works and is actually more understandable to a wider audience.
我使用2种方法。一种是迭代列表,一种是使用惰性评估。我根据情况使用它们。
I use 2 methods. One iterates the list, one works with lazy eval. I use them as the situation defines.
您可以为
IEnumerable
编写自己的扩展方法,如下所示:Linq 中不存在这样的方法,因为 Linq 基本上用于查询,而不是简单的迭代。
另请注意,当使用实际的
List
实例时,扩展方法不会被调用,因为实例方法在共享签名时优先于扩展方法。
例如,以下代码不会调用扩展方法:
而以下代码将:
You can write your own extension method for
IEnumerable<T>
like so:Such a method is not present in Linq, because Linq is basically meant for queries, not for simple iterations.
Also, note that when using an actual
List<T>
instance,the extension method will not be called because instance methods have precedence over extension methods when they share signature.
The following code, for example, would not invoke the extension method:
Whereas the following would:
您可以编写自己的扩展方法 ToList(this List theList){return theList;} ,然后避免开销。由于您的扩展方法是最具体的一种,它将被调用,而不是 IEnumerable 上的方法
You can write your own extension method ToList(this List theList){return theList;} and then avoid the overhead. Since your extension method is the most specific one it will be called, not the one on IEnumerable
如果您决定通过扩展方法来执行此操作,我会将其称为 ForAll 而不是 ForEach。这是为了使用与 并行扩展相同的语法.NET 4.0 使用:
If you decide to do this via an extension method, I'd call it ForAll instead of ForEach. This in order to use the same syntax as the Parallel Extensions in .NET 4.0 use: