您更喜欢哪一个?
foreach(var zombie in zombies)
{
zombie.ShuffleTowardsSurvivors();
zombie.EatNearbyBrains();
}
或
zombies.Each(zombie => {
zombie.ShuffleTowardsSurvivors();
zombie.EatNearbyBrains();
});
答案 0 :(得分:26)
第一个。出于某种原因,这是语言的一部分。
就个人而言,如果有充分的理由,我只会使用第二种功能方法进行流控制,例如在.NET 4中使用Parallel.ForEach
。它有许多缺点,包括:
foreach (..) { myDelegate(); }
我认为没有理由为语言中已存在的流控制结构编写自己的语法。
答案 1 :(得分:10)
这里你正在做一些非常必要的事情,比如编写一个语句而不是一个表达式(可能是Each
方法没有返回任何值)和变异状态(只能假设这些方法,因为它们也是似乎没有返回值)但是你试图通过将一组语句作为委托传递来将它们作为“函数式编程”传递出去。这段代码几乎不能远离函数式编程的理想和习语,那么为什么要试着把它伪装成呢?
尽管我喜欢C#这样的多范式语言,但我认为当范式在更高层次上混合时(例如,以功能或命令式风格编写的整个方法),它们最容易理解和维护,而不是当多个范例在单个语句或表达式中混合时。
如果您正在编写命令性代码,请诚实地使用它并使用循环。没什么值得羞耻的。命令式代码本质上不是坏事。
答案 2 :(得分:5)
第二种形式。
在我看来,你必须使用的语言结构和关键词越少越好。 C#中有足够多的外来瑕疵。
通常,您输入的越少越好。说真的,你怎么不想在这样的情况下使用“var”?当然,如果明确是你唯一的目标,你仍然会使用匈牙利表示法...你有一个IDE,当你将鼠标悬停在你身上时会给你输入类型信息......当然,如果你使用的是Resharper,还是按Ctrl + Q.
@ T.E.D。委托调用的性能影响是次要问题。如果你确实这样做了一千个术语,请运行点跟踪并查看它是否不可接受。
@ Reed Copsey:非标准,如果开发人员无法解决“。每个”正在做什么,那么你会遇到更多问题,呵呵。攻击语言以使其更好是编程的一大乐趣。
答案 3 :(得分:4)
lamda版本实际上并不慢。我只是进行了快速测试,代理版本的速度提高了约30%。
这是codez:
class Blah {
public void DoStuff() {
}
}
List<Blah> blahs = new List<Blah>();
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));
好的,所以我运行了更多测试,结果就是这里。
执行的顺序(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
对于类的数组,性能差异要小得多。以下是Blah [30000000]的数字:
lambda - 00:00:00.317 foreach - 00:00:00.337
这是相同的测试,但Blah是一个结构:
Blah[] version lambda - 00:00:00.676 foreach - 00:00:00.437 List version: lambda - 00:00:00.461 foreach - 00:00:00.391
优化构建,Blah是一个使用数组的结构。
lambda - 00:00:00.426 foreach - 00:00:00.079
结论:对于foreach vs lambda的表现没有全面的答案。答案是取决于。 Here是对List<T>
进行更科学的测试。据我所知,它非常高效。如果您真的关心性能使用for(int i...
循环。为了迭代一千个客户记录的集合(例子),这真的无关紧要。
至于决定使用哪个版本,我会在lambda版本的底部放置潜在的性能。
结论#2 T[]
(其中T是值类型)在优化的构建中,foreach循环的速度大约是此测试的5倍。这是Debug和Release构建之间唯一的重要区别。所以你去,对于值类型的数组使用foreach,其他一切 - 这没关系。
答案 4 :(得分:1)
This question包含一些有用的讨论,以及关于该主题的哲学方面的MSDN博客文章的链接。
答案 5 :(得分:1)
我认为扩展方法很酷,但我认为break
和编辑并继续更酷。
答案 6 :(得分:0)
我认为第二种形式更难以优化,因为对于这一次调用,编译器无法以任何其他方式调用每个方法调用循环。
既然有人问我,我会详细说明。该方法的实现很容易与调用它的代码分开编译。这意味着编译器并不确切知道要执行多少循环。
如果您使用“foreach”表单,则在创建循环代码时,可能可以使用该信息(它也可能不可用,在这种情况下没有区别)
例如,如果编译器碰巧知道(来自同一文件中的先前代码)列表中恰好有20个项目,它可以用20个引用替换整个循环。
但是,当编译器在其源文件中为“Every”方法创建代码时,它不知道调用者列表的大小。它必须支持任何规模。它能做的最好的事情就是尝试为其CPU找到某种最佳的展开方式,并添加额外的代码来循环 ,如果它对于展开而言太小,则进行适当的循环。对于典型的小循环,这甚至可能最终更慢。当然,对于小循环,你并不在乎......除非它们碰巧在一个大循环中。
正如另一张海报所提到的,这是(并且应该是)次要问题。重要的是哪个更容易阅读和/或维护,但我没有看到那里的巨大差异。
答案 7 :(得分:-4)
我也不喜欢,因为我认为不需要使用'var'。我写的是:
foreach(Zombie zombie in zombies){
}
但至于功能还是预告,对我来说,我绝对更喜欢foreach,因为后者似乎没有充分的理由。