C#foreach vs functional各

您更喜欢哪一种?

foreach(var zombie in zombies) { zombie.ShuffleTowardsSurvivors(); zombie.EatNearbyBrains(); } 

要么

 zombies.Each(zombie => { zombie.ShuffleTowardsSurvivors(); zombie.EatNearbyBrains(); }); 

第二种forms。

在我看来,你必须使用的语言结构和关键字越少越好。 C#中有足够的无关紧要的东西。

通常,您输入的越少越好。 说真的,你怎么不想在这样的情况下使用“var”? 当然,如果明确是你唯一的目标,你仍然会使用匈牙利语表示法…你有一个IDE,只要你将鼠标hover在你身上就可以提供类型信息……当然,如果你使用的是Resharper,还是按Ctrl + Q. 。

@TED委托调用的性能影响是次要问题。 如果你这样做一千个条款肯定,运行点跟踪,看看它是否不被接受。

@Reed Copsey:非标准,如果开发人员无法解决“。每个”正在做什么,那么你会遇到更多问题,呵呵。 破解语言以使其更好是编程的一大乐趣。

首先。 出于某种原因,它是语言的一部分。

就个人而言,如果有充分的理由,我只会使用第二种function方法进行流量控制,例如在.NET 4中使用Parallel.ForEach 。它有许多缺点,包括:

  • 它慢了。 它将在每个元素上引入一个委托调用,就像你执行foreach (..) { myDelegate(); } foreach (..) { myDelegate(); }
  • 这是非标准的,因此大多数开发人员都会更难理解
  • 如果你关闭任何本地人,你将强制编译器进行关闭。 如果涉及到线程,这可能会导致奇怪的问题,并且会给程序集增加完全不必要的膨胀。

我认为没有理由为语言中已存在的流控制构造编写自己的语法。

在这里你做了一些非常必要的事情,比如写一个语句而不是一个表达式(大概是Each方法都没有返回值)和变异状态(哪个只能假设方法这样做,因为它们似乎也没有返回任何值)您试图通过将一组语句作为委托传递来将它们作为“函数式编程”传递出去。 这段代码几乎不能远离函数式编程的理想和习语,那么为什么要试着把它伪装成这样呢?

尽管我喜欢C#这样的多范式语言,但我认为当范式在更高层次上混合时(例如,以function或命令式编写的整个方法)而不是多个范例时,它们最容易理解和维护。在单个语句或表达式中混合使用。

如果您正在编写命令式代码,请诚实地使用它并使用循环。 没什么值得羞耻的。 命令式代码本质上不是坏事。

lamda版本实际上并不慢。 我只是进行了快速测试,代理版本的速度提高了约30%。

这是codez:

 class Blah { public void DoStuff() { } } List blahs = new List(); DateTime start = DateTime.Now; for(int i = 0; i < 30000000; i++) { blahs.Add(new Blah()); } TimeSpan elapsed = (DateTime.Now - start); Console.WriteLine(string.Format(System.Globalization.CultureInfo.CurrentCulture, "Allocation - {0:00}:{1:00}:{2:00}.{3:000}", elapsed.Hours, elapsed.Minutes, elapsed.Seconds, elapsed.Milliseconds)); start = DateTime.Now; foreach(var bl in blahs) { bl.DoStuff(); } elapsed = (DateTime.Now - start); Console.WriteLine(string.Format(System.Globalization.CultureInfo.CurrentCulture, "foreach - {0:00}:{1:00}:{2:00}.{3:000}", elapsed.Hours, elapsed.Minutes, elapsed.Seconds, elapsed.Milliseconds)); start = DateTime.Now; blahs.ForEach(bl=>bl.DoStuff()); elapsed = (DateTime.Now - start); Console.WriteLine(string.Format(System.Globalization.CultureInfo.CurrentCulture, "lambda - {0:00}:{1:00}:{2:00}.{3:000}", elapsed.Hours, elapsed.Minutes, elapsed.Seconds, elapsed.Milliseconds)); 

好的,所以我运行了更多测试,结果如下。

  1. 执行的顺序(forach,lambda或lambda,foreach)没有太大的区别,lambda版本仍然更快:

      foreach  -  00:00:00.561
     lambda  -  00:00:00.389
    
     lambda  -  00:00:00.317
     foreach  -  00:00:00.337 
  2. 对于类的数组,性能的差异要小得多。 以下是Blah [30000000]的数字:

      lambda  -  00:00:00.317 
     foreach  -  00:00:00.337 
  3. 这是相同的测试,但Blah是一个结构:

      Blah []版 
     lambda  -  00:00:00.676 
     foreach  -  00:00:00.437 
    
    清单版本:
     lambda  -  00:00:00.461
     foreach  -  00:00:00.391 
  4. 优化构建,Blah是一个使用数组的结构。

      lambda  -  00:00:00.426
     foreach  -  00:00:00.079 

结论:对于foreach vs lambda的表现没有全面的答案。 答案取决于它 。 这是List的更科学的测试。 据我所知,这非常高效。 如果你真的关心性能的使用for(int i... loop。对于迭代一千个客户记录的集合(例子),那真的无关紧要。

至于决定使用哪个版本,我会在lambda版本的底部放置潜在的性能。

结论#2 T[] (其中T是值类型)foreach循环在优化构建中的此测试快约5倍。 这是Debug和Release构建之间唯一的重要区别。 所以你去,对于值类型的数组使用foreach,其他一切 – 这没关系。

这个问题包含一些有用的讨论,以及MSDN博客文章的链接,关于该主题的哲学方面。

我认为扩展方法很酷,但我认为break和edit-and-continue更酷。

我认为第二种forms更难以优化,因为对于这一次调用,编译器无法以任何其他方式调用Each方法来展开循环。


既然有人问过,我会详细说明。 该方法的实现很容易与调用它的代码分开编译。 这意味着编译器并不确切知道它将要执行多少循环。

如果您使用“foreach”表单,那么在为循环创建代码时,该信息可能对编译器有用(它也可能不可用,在这种情况下没有区别)。

例如,如果编译器恰好知道(来自同一文件中的先前代码)列表中恰好有20个项目,则它可以用20个引用替换整个循环。

但是,当编译器在其源文件中为“每个”方法创建代码时,它不知道调用者列表的大小。 它必须支持任何规模。 它能做的最好的事情就是尝试为它的CPU找到某种最佳的展开方式,并添加额外的代码来循环使用 ,如果它对于展开而言太小则可以进行适当的循环。 对于典型的小循环,这甚至可能最终变慢 。 当然对于小循环你并不在乎……除非它们碰巧在一个大循环中。

正如另一张海报所提到的,这是(并且应该是)次要问题。 重要的是哪个更容易阅读和/或维护,但我没有看到那里的巨大差异。

我也不喜欢,因为我认为不需要使用’var’。 我写的是:

 foreach(Zombie zombie in zombies){ } 

但至于function还是预告,对我来说,我绝对更喜欢foreach,因为后者似乎没有充分的理由。