显然有很多方法可以迭代集合.好奇,如果有任何差异,或为什么你使用一种方式而不是另一种方式.
第一种:
ListsomeList = foreach(string s in someList) { }
另一种方式:
ListsomeList = someList.ForEach(delegate(string s) { });
我认为,除了我上面使用的匿名代表之外,我还有一个你可以指定的可重用代理......
两者之间有一个重要且有用的区别.
因为.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);
tl; dr:请勿将此代码复制到您的应用程序中!
这些例子不是最好的做法,他们只是为了演示之间的差异ForEach()
和foreach
.
从for
循环内的列表中删除项目可能会产生副作用.最常见的一个在这个问题的评论中描述.
通常,如果您要从列表中删除多个项目,则需要将确定要从实际删除中删除的项目分开.它不会保持您的代码紧凑,但它保证您不会错过任何项目.
我们在这里度过了一段代码(在VS2005和C#2.0)在先前工程师走出自己使用的方式list.ForEach( delegate(item) { foo;});
,而不是foreach(item in list) {foo; };
对所有他们编写的代码.例如,用于从dataReader读取行的代码块.
我仍然不知道为什么他们这样做.
缺点list.ForEach()
是:
它在C#2.0中更加冗长.但是,在C#3之后,您可以使用" =>
"语法来制作一些非常简洁的表达式.
它不太熟悉.必须维护此代码的人会想知道为什么你这样做.我花了一段时间才决定没有任何理由,除了可能让作家看起来很聪明(其余代码的质量破坏了这一点).它的可读性也较低,})
代理代码块末尾的" ".
另请参阅Bill Wagner的书"有效的C#:50改进C#的具体方法",其中他谈到为什么foreach比其他循环更喜欢for或while循环 - 主要的一点是你让编译器决定构建的最佳方法循环.如果未来版本的编译器设法使用更快的技术,那么您将通过使用foreach和rebuilding而不是更改代码来免费获得此技术.
一个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)
为了坚持常用和可读性而使用大多数循环,并且list.Foreach()
只能用于短块,当你可以使用C#3" =>
"操作符更优雅或更紧凑地执行操作时.在这种情况下,可能已经有一个更具体的LINQ扩展方法ForEach()
.看看Where()
,Select()
,Any()
,All()
,Max()
或许多其他LINQ方法中的一种尚不你从循环想要的东西.
为了好玩,我将List弹出到反射器中,这就是生成的C#:
public void ForEach(Actionaction) { if (action == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); } for (int i = 0; i < this._size; i++) { action(this._items[i]); } }
同样,foreach使用的Enumerator中的MoveNext是这样的:
public bool MoveNext() { if (this.version != this.list._version) { ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); } if (this.index < this.list._size) { this.current = this.list._items[this.index]; this.index++; return true; } this.index = this.list._size + 1; this.current = default(T); return false; }
List.ForEach比MoveNext更加严格 - 处理更少 - 更有可能JIT变成高效的东西.
另外,无论如何,foreach()都会分配一个新的枚举器.GC是你的朋友,但如果你反复做同样的事情,这会产生更多的一次性物品,而不是重复使用同一个代表 - 但是 - 这实际上是一个边缘情况.在典型的使用中,您将看到很少或没有区别.
我知道两件让他们与众不同的晦涩难懂的事物.去吧!
首先,存在为列表中的每个项目制作委托的经典错误.如果使用foreach关键字,则所有代理最终都可以引用列表的最后一项:
// A list of actions to execute later Listactions = new List (); // Numbers 0 to 9 List numbers = Enumerable.Range(0, 10).ToList(); // Store an action that prints each number (WRONG!) foreach (int number in numbers) actions.Add(() => Console.WriteLine(number)); // Run the actions, we actually print 10 copies of "9" foreach (Action action in actions) action(); // So try again actions.Clear(); // Store an action that prints each number (RIGHT!) numbers.ForEach(number => actions.Add(() => Console.WriteLine(number))); // Run the actions foreach (Action action in actions) action();
List.ForEach方法没有此问题.迭代的当前项通过值作为参数传递给外部lambda,然后内部lambda在其自己的闭包中正确捕获该参数.问题解决了.
(遗憾的是我相信ForEach是List的成员,而不是扩展方法,虽然很容易自己定义它,所以你可以在任何可枚举的类型上使用它.)
其次,ForEach方法方法有局限性.如果使用yield return实现IEnumerable,则无法在lambda中执行yield return.因此,通过此方法无法循环遍历集合中的项目以产生返回事物.您必须使用foreach关键字并通过手动在循环内复制当前循环值来解决闭包问题.
更多这里
我想这个someList.ForEach()
调用可以很容易地并行化,而法线foreach
并不容易并行.您可以轻松地在不同的核心上运行几个不同的代理,这对于普通代码来说并不容易foreach
.
只需2美分