foreach vs someList.ForEach(){}
显然有很多方法可以迭代集合。 好奇有没有什么不同,或者你为什么会用另一种方式。
第一类:
List<string> someList = <some way to init>
foreach(string s in someList) {
<process the string>
}
另一种方式:
List<string> someList = <some way to init>
someList.ForEach(delegate(string s) {
<process the string>
});
我想我的头顶上,而不是我上面使用的匿名委托,你会有一个可以指定的可重用委托...
两者之间有一个重要且有用的区别。
因为.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; };
foreach(item in list) {foo; };
为他们写的所有代码。 例如用于从dataReader读取行的代码块。
我仍然不知道他们为什么这样做。
list.ForEach()
的缺点是:
它在C#2.0中更加冗长。 但是,在C#3以后,可以使用“ =>
”语法来制作一些很好的简洁表达式。
它不太熟悉。 必须维护这些代码的人会想知道为什么你这样做。 我花了一段时间才决定没有任何理由,除了让作者看起来很聪明(其他代码的质量破坏了这一点)。 它的可读性也较差,“ })
”在委托代码块的末尾。
另请参阅Bill Wagner的书“Effective C#:50 Specific Ways to Improve Your 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)
“确切地说明了你想如何完成” - a for(int 1 = 0; i < count; i++)
循环会这样做, foreach
循环会将控制权选择为编译器。
我的感觉是,在一个新项目中,为了遵守常见用法和可读性,在大多数循环中使用foreach(item in list)
,并且只使用list.Foreach()
仅用于短块,当你可以做更多的事情时优雅地或紧凑地与C#3“ =>
”运营商。 在这种情况下,可能已经存在比ForEach()
更具体的LINQ扩展方法。 看看Where()
, Select()
, Any()
, All()
, Max()
或其他许多LINQ方法之一是否还没有做到你想要的循环。
为了好玩,我将List弹出到了反射器中,这是由此产生的C#:
public void ForEach(Action<T> action)
{
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,这将使更多的一次性的对象,而不是重用相同的委托- 但是 -这实在是一个边缘的情况下。 在典型的使用情况下,您将看到很少或没有差异。
链接地址: http://www.djcxy.com/p/5143.html