【问题标题】:.Net - When is List<T>.ForEach prefered over a standard foreach loop?.Net - List<T>.ForEach 何时优于标准 foreach 循环?
【发布时间】:2009-07-23 16:35:26
【问题描述】:

通用列表类有一个.ForEach(Action&lt;T&gt; action) 方法。现在我已经对它们的性能做了一些简单时间安排,似乎通用的 ForEach 性能较差。 (代码段编译器友好)代码如下 -

public static class timer{
    public static long foreachloop = 0;
    public static long Gforeachloop = 0;}

public class something{
    public List<string> myStrings = new List<string>();

    public something()
    {
        for(int i = 1; i<=5000000;i++)
        {
            myStrings.Add(i.ToString());
        }
    }}

public class cls1{
    private static List<string> Strings = new List<string>();
    private static List<string> OtherStrings = new List<string>();

    public static void RunSnippet()
    {
        something s = new something();

        Stopwatch watch = new Stopwatch();
        watch.Start();
        foreach(string x in s.myStrings)
        {
            Strings.Add(x);
        }
        watch.Stop();
        timer.foreachloop = watch.ElapsedMilliseconds;

        watch.Reset();
        watch.Start();

        s.myStrings.ForEach(delegate(string n){OtherStrings.Add(n);});

        s.myStrings.Clear();

        watch.Stop();
        timer.Gforeachloop = watch.ElapsedMilliseconds;

        WL("FOREACH-"+timer.foreachloop + ",Count = " + Strings.Count);
        WL("GFOREACH-"+timer.Gforeachloop + ",Count = " + OtherStrings.Count);
    }

    #region Helper methods

    public static void Main()
    {
        try
        {
            RunSnippet();
        }
        catch (Exception e)
        {
            string error = string.Format("---\nThe following error occurred while executing the snippet:\n{0}\n---", e.ToString());
            Console.WriteLine(error);
        }
        finally
        {
            Console.Write("Press any key to continue...");
            Console.ReadKey();
        }
    }

    private static void WL(object text, params object[] args)
    {
        Console.WriteLine(text.ToString(), args);   
    }

    private static void RL()
    {
        Console.ReadLine(); 
    }

    private static void Break() 
    {
        System.Diagnostics.Debugger.Break();
    }

    #endregion
}

FOREACH 在 177 毫秒出现,GFOREACH 在 707 毫秒出现。

现在我猜有一个很好的理由使用它,但我就是想不出一个。显然性能不是原因,所以问题是什么时候它会是最佳选择?

提前致谢。

【问题讨论】:

标签: c# performance generics generic-list


【解决方案1】:

Eric Lippert 的这篇博文提供了背景:

http://blogs.msdn.com/ericlippert/archive/2009/05/18/foreach-vs-foreach.aspx

他说的是扩展方法的常见建议,可以为IEnumerable&lt;T&gt; 做同样的事情,但哲学上的反对意见也适用于List&lt;T&gt;.ForEach

这表明也许这种方法从来都不是一个好主意,尽管它看起来“很酷”。使用foreach 会更清楚。

我建议可以将此类方法视为a fix for the classic closure-over-loop-variable bug

但在实践中,我在发现此类错误方面做得更好。

【讨论】:

  • 他主要在IEnumerable&lt;T&gt; 上谈论.ForEach(你在其中链接和组合东西),而不是在List&lt;T&gt; 上你想在单行中对列表中的每个对象执行一个方法.
  • 但是对于ForEach 上的IEnumerable&lt;T&gt; 是可组合的,并没有广泛认同的意义。显而易见,一致的实现是模仿List&lt;T&gt;.ForEach 并返回 void,正如 Eric 在他的博客文章示例中所做的那样。因此,同样的哲学反对意见适用于两者。
  • 关于循环变量闭包问题,我只希望他们让匿名委托和 lambdas 对循环变量“智能”,默认捕获它们的副本。在编码人员打算从 lambda 内部修改循环变量的极少数情况下,他们可以围绕它进行编码。另外 99% 的时间,我们的生活会更轻松。我想这是现​​在很难解决的事情之一。
  • 可能。你的推理是正确的IMO。然而,他们决定拥有List&lt;T&gt;.ForEachArray.ForEach(两者都出现在.NET 2.0 中)并且他们没有提供Enumerable.ForEach extension 方法(在3.5 中)的事实让我觉得这些有不同的哲学原因。基本上,我猜List&lt;T&gt;.ForEach 旨在用于已经存在的 集合,而不是查询的结果。顺便说一句,我认为性能他们为框架中的数组和列表提供ForEach的原因之一
  • 我认为 Eric 博客上的那篇文章中的这句话证明了这一点:“我不适合制作唯一一个仅对其有用的 sequence operator副作用。” [强调补充]。显然,在 2.0 中添加 Array.ForEachList&lt;T&gt;.ForEach 时,它们打算用作序列运算符;所以博客条目并不真正适用于他们。
【解决方案2】:

当它看起来更整洁时。

根本不是玩笑。真的,我是认真的。在您的情况下使用更具可读性的样式。例如,如果您只想在每个项目上调用一个方法,例如:

list.ForEach(Console.WriteLine);

这种风格更适合。但是,如果您有一百行作为循环体,或者您有嵌套循环和控制流结构,那么旧样式看起来会更好。

【讨论】:

  • 对我来说看起来很奇怪。我会发现标准 foreach 更容易在视觉上使用。
  • AnthonyWJones:我认为这主要是因为我们习惯了命令式编程。对于使用函数式语言的程序员来说,这种风格肯定看起来更自然。
  • @Mehrdad - 不是真的。看看哈斯克尔。在编写带有副作用的命令式代码时(这就是全部内容),他们使用一种特殊的 monad 语法来模仿命令式语言的风格。
  • 哦,我以为你的意思是一种真正的函数式语言! :P
  • @Mehrdad:是的,我习惯于命令式编程,C# 本质上主要是命令式的。最近,它开始支持不断增长的函数式编程风格。这就是问题所在,我想要我熟悉的东西,这些东西必须继续看起来像往常一样,并且功能性的东西看起来很实用。它帮助我区分这些非常不同的思维方式。
猜你喜欢
  • 2016-08-27
  • 2010-10-19
  • 2010-10-24
  • 1970-01-01
  • 2012-07-14
  • 2017-07-14
  • 2010-12-27
  • 2012-06-12
  • 1970-01-01
相关资源
最近更新 更多