List.ForEach 与自定义 IEnumerable扩大

发布于 2024-08-22 00:53:20 字数 1175 浏览 7 评论 0原文

假设我有一个类:

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

不打扰别人 2024-08-29 00:53:20

您可以编写扩展方法,但有好的ForEach 未在 IEnumerable 上实现的原因。第二个示例

result.ToList().ForEach(DoSomething);

将 IEnumerable 复制到列表中(我假设它已经是一个列表),因此您最好使用旧的 foreach(var r in result) {} 迭代 IEnumerable。

附录:

对我来说,Eric Lippert 文章的要点是添加 ForEach 没有任何好处,而且增加了一些潜在的陷阱:

第二个原因是这样做
添加零新的代表性权力
到语言。这样做可以让你
重写这段完全清晰的代码:

foreach(Foo foo in foos){ 语句
涉及 foo; }

进入这段代码:

foos.ForEach((Foo foo)=>{ 语句
涉及 foo; });

使用几乎完全相同的
角色略有不同
命令。然而第二个版本是
更难理解,更难调试,
并引入闭包语义,
从而可能改变对象
以微妙的方式度过一生。

You can write an extension method but there are good reasons why ForEach is not implemented on IEnumerable<T>. The second example

result.ToList().ForEach(DoSomething);

copies 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:

The second reason is that doing so
adds zero new representational power
to the language. Doing this lets you
rewrite this perfectly clear code:

foreach(Foo foo in foos){ statement
involving foo; }

into this code:

foos.ForEach((Foo foo)=>{ statement
involving foo; });

which uses almost exactly the same
characters in slightly different
order. And yet the second version is
harder to understand, harder to debug,
and introduces closure semantics,
thereby potentially changing object
lifetimes in subtle ways.

九局 2024-08-29 00:53:20

我更喜欢这个:-

foreach (var item in result.ToList())
{
   DoSomething(item);
}

这是一个更清晰的习惯用法,它表示将一系列内容收集在一起然后做一些可能改变应用程序状态的重要事情。这是一种老派的做法,但它很有效,而且实际上更容易为更广泛的受众所理解。

I would prefer this:-

foreach (var item in result.ToList())
{
   DoSomething(item);
}

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.

彻夜缠绵 2024-08-29 00:53:20

我使用2种方法。一种是迭代列表,一种是使用惰性评估。我根据情况使用它们。

    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;
    }

I use 2 methods. One iterates the list, one works with lazy eval. I use them as the situation defines.

    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;
    }
漆黑的白昼 2024-08-29 00:53:20

您可以为 IEnumerable 编写自己的扩展方法,如下所示:

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

Linq 中不存在这样的方法,因为 Linq 基本上用于查询,而不是简单的迭代。

另请注意,当使用实际的 List 实例时,
扩展方法不会被调用,因为实例方法在共享签名时优先于扩展方法。

例如,以下代码不会调用扩展方法:

 var l = new List<MyClass>();
 l.Add(new MyClass());
 l.ForEach(DoSomething);

而以下代码将:

IEnumerable<MyClass> l = new List<MyClass>(new []{new MyClass()});
l.ForEach(DoSomething);  

You can write your own extension method for IEnumerable<T> like so:

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

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:

 var l = new List<MyClass>();
 l.Add(new MyClass());
 l.ForEach(DoSomething);

Whereas the following would:

IEnumerable<MyClass> l = new List<MyClass>(new []{new MyClass()});
l.ForEach(DoSomething);  
吖咩 2024-08-29 00:53:20

您可以编写自己的扩展方法 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

挽容 2024-08-29 00:53:20

如果您决定通过扩展方法来执行此操作,我会将其称为 ForAll 而不是 ForEach。这是为了使用与 并行扩展相同的语法.NET 4.0 使用:

var result = // web service call
result.AsParallel().ForAll(DoSomething);

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:

var result = // web service call
result.AsParallel().ForAll(DoSomething);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文