循环语句和List类的ForEach()方法
循环语句和List类的ForEach()方法
显然有许多方法可以迭代一个集合。好奇有什么区别,或者为什么会使用一种方法而不是另一种方法。
第一种类型:
List someList = foreach(string s in someList) { }
另一种方法:
List someList = someList.ForEach(delegate(string s) { });
我想,从我的头脑中想象,与我上面使用的匿名委托不同,你将有一个可重用的委托你可以指定...
我们有一些代码(在VS2005和C#2.0中),其中先前的工程师费尽心思地使用list.ForEach(delegate(item){foo; });
而不是 foreach(item in list){foo; };
编写他们所编写的所有代码。例如,从数据读取器中读取行的代码块。
我仍然不知道他们为什么这样做。
list.ForEach()
的缺点是:
它在C# 2.0中更为冗长。但是,在C#3及以后,您可以使用“=>
”语法以使一些表达式变得更加简洁。
它不太熟悉。必须维护此代码的人们会想知道为什么要那样做。我花了一段时间才确定没有任何理由,除了可能让作者显得聪明(其余代码的质量削弱了这一点)。它也不太可读,委托代码块末尾有“})
”。
另请参见比尔·瓦格纳的书《Effective C#:50种改善C#的特定方法》,在其中他谈到为什么foreach比其他循环(如for或while循环)更受青睐的原因-主要是因为您让编译器决定构建循环的最佳方法。如果将来的编译器版本成功使用更快的技术,则使用foreach并重新构建会免费获得此功能,而不是更改代码。
foreach(item in list)
结构允许您使用 break
或 continue
如果需要退出迭代或循环。但是,您无法在foreach循环内更改列表。
我惊讶地发现,list.ForEach
略快。但这可能不是使用它的有效原因,这将是过早的优化。如果您的应用程序使用数据库或Web服务,而不是循环控件,几乎总是会消耗时间。您还对 for
循环进行了基准测试吗?list.ForEach
可能会由于在内部使用它并且没有包装器的 for
循环而更快。
我不同意使用 list.ForEach(delegate)
的版本在任何有意义的程度上更具"函数式"风格。 尽管它将一个函数传递给另一个函数,但结果或程序组织并没有太大的区别。
我认为使用 foreach(item in list)
不一定能够“准确说明你想如何完成任务” —— for(int 1 = 0; i < count; i++)
循环能够做到这一点,而 foreach
循环将控制权留给编译器去决定。
在新项目中,我倾向于使用 foreach(item in list)
做大多数的循环,以符合通用用法并提高可读性,而只有在需要使用 C# 3 的 "=>
" 操作符更加优雅和简洁的短小代码块时再使用 list.Foreach()
。在这种情况下,可能已经有比 ForEach()
更具体的 LINQ 扩展方法实现了你想要的循环功能。可以查看一下是否已经有 Where()
, Select()
, Any()
, All()
, Max()
或许多其他 LINQ 方法实现了你所需的循环功能。
这两者之间有一个重要且有用的区别。
因为 .ForEach 使用 for 循环来迭代集合,所以以下代码是有效的 (编辑:在 .net 4.5 之前的版本有效,实现发生了变化,它们都会抛出异常):
someList.ForEach(x => { if(x.RemoveMe) someList.Remove(x); });
而 foreach 使用枚举器,因此以下代码不是有效的:
foreach(var item in someList) if(item.RemoveMe) someList.Remove(item);
简而言之:不要把这段代码复制粘贴到你的应用程序中!
这些示例不是最佳实践,只是为了演示 ForEach() 和 foreach 之间的区别。
在 for 循环内从列表中移除项可能会产生副作用,最常见的副作用在这个问题的评论中有描述。
一般来说,如果你想从列表中移除多个项,你会想要将确定要移除的项与实际移除分开。这不会使代码更简洁,但它保证你不会错过任何项。