C# foreach 与 someList.ForEach(){}
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/225937/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
foreach vs someList.ForEach(){}
提问by Bryce Fischer
There are apparently many ways to iterate over a collection. Curious if there are any differences, or why you'd use one way over the other.
显然有很多方法可以迭代一个集合。好奇是否有任何差异,或者为什么你会使用一种方式而不是另一种方式。
First type:
第一种:
List<string> someList = <some way to init>
foreach(string s in someList) {
<process the string>
}
Other Way:
另一种方式:
List<string> someList = <some way to init>
someList.ForEach(delegate(string s) {
<process the string>
});
I suppose off the top of my head, that instead of the anonymous delegate I use above, you'd have a reusable delegate you could specify...
我想我的头顶上,不是我上面使用的匿名委托,而是您可以指定一个可重用的委托......
回答by Joachim Kerschbaumer
I guess the someList.ForEach()
call could be easily parallelized whereas the normal foreach
is not that easy to run parallel.
You could easily run several different delegates on different cores, which is not that easy to do with a normal foreach
.
Just my 2 cents
我想这个someList.ForEach()
调用可以很容易地并行化,而正常的foreach
并行运行并不那么容易。您可以轻松地在不同的内核上运行多个不同的委托,这对于普通的foreach
.
只是我的 2 美分
回答by Craig.Nicol
You could name the anonymous delegate :-)
您可以命名匿名委托 :-)
And you can write the second as:
您可以将第二个写为:
someList.ForEach(s => s.ToUpper())
Which I prefer, and saves a lot of typing.
我更喜欢,并且可以节省很多打字时间。
As Joachim says, parallelism is easier to apply to the second form.
正如 Joachim 所说,并行性更容易应用于第二种形式。
回答by EFrank
The second way you showed uses an extension method to execute the delegate method for each of the elements in the list.
您展示的第二种方法使用扩展方法为列表中的每个元素执行委托方法。
This way, you have another delegate (=method) call.
这样,您就有了另一个委托(=方法)调用。
Additionally, there is the possibility to iterate the list with a forloop.
此外,还可以使用for循环迭代列表。
回答by jezell
Behind the scenes, the anonymous delegate gets turned into an actual method so you could have some overhead with the second choice if the compiler didn't choose to inline the function. Additionally, any local variables referenced by the body of the anonymous delegate example would change in nature because of compiler tricks to hide the fact that it gets compiled to a new method. More info here on how C# does this magic:
在幕后,匿名委托变成了一个实际的方法,因此如果编译器没有选择内联该函数,您可能会对第二个选择产生一些开销。此外,匿名委托示例的主体引用的任何局部变量在本质上都会发生变化,因为编译器会隐藏它被编译为新方法的事实。更多关于 C# 如何实现这种魔法的信息:
http://blogs.msdn.com/oldnewthing/archive/2006/08/04/688527.aspx
http://blogs.msdn.com/oldnewthing/archive/2006/08/04/688527.aspx
回答by Chris Kimpton
One thing to be wary of is how to exit from the Generic .ForEach method - see this discussion. Although the link seems to say that this way is the fastest. Not sure why - you'd think they would be equivalent once compiled...
需要注意的一件事是如何退出 Generic .ForEach 方法 - 请参阅此讨论。虽然链接好像说这种方式最快。不知道为什么 - 你会认为它们一旦编译就等价了......
回答by Joel Coehoorn
List.ForEach() is considered to be more functional.
List.ForEach() 被认为更具功能性。
List.ForEach()
says what you want done. foreach(item in list)
also says exactly how you want it done. This leaves List.ForEach
free to change the implementation of the howpart in the future. For example, a hypothetical future version of .Net might always run List.ForEach
in parallel, under the assumption that at this point everyone has a number of cpu cores that are generally sitting idle.
List.ForEach()
说你想做什么。 foreach(item in list)
还准确地说明了您希望如何完成。这样List.ForEach
可以在将来自由更改如何部分的实现。例如,List.ForEach
假设在这一点上每个人都有许多通常闲置的 cpu 内核,假设的 .Net 的未来版本可能总是并行运行。
On the other hand, foreach (item in list)
gives you a little more control over the loop. For example, you know that the items will be iterated in some kind of sequential order, and you could easily break in the middle if an item meets some condition.
另一方面,foreach (item in list)
让您对循环有更多的控制。例如,您知道项目将按某种顺序迭代,如果项目满足某些条件,您很容易在中间中断。
Some more recent remarks on this issue are available here:
此处提供了有关此问题的一些最新评论:
回答by Joel Coehoorn
There is one important, and useful, distinction between the two.
两者之间有一个重要且有用的区别。
Because .ForEach uses a for
loop to iterate the collection, this is valid (edit: prior to .net 4.5- the implementation changed and they both throw):
因为 .ForEach 使用for
循环来迭代集合,这是有效的(编辑:在 .net 4.5 之前- 实现改变并且它们都抛出):
someList.ForEach(x => { if(x.RemoveMe) someList.Remove(x); });
whereas foreach
uses an enumerator, so this is not valid:
而foreach
使用枚举器,所以这是无效的:
foreach(var item in someList)
if(item.RemoveMe) someList.Remove(item);
tl;dr: Do NOT copypaste this code into your application!
tl; dr:不要将此代码复制粘贴到您的应用程序中!
These examples aren't best practice, they are just to demonstrate the differences between ForEach()
and foreach
.
这些例子不是最好的做法,他们只是为了演示之间的差异ForEach()
和foreach
。
Removing items from a list within a for
loop can have side effects. The most common one is described in the comments to this question.
从for
循环内的列表中删除项目可能会产生副作用。对此问题的评论中描述了最常见的一种。
Generally, if you are looking to remove multiple items from a list, you would want to separate the determination of which items to remove from the actual removal. It doesn't keep your code compact, but it guarantees that you do not miss any items.
通常,如果您希望从列表中删除多个项目,您可能希望将确定删除哪些项目与实际删除分开。它不会使您的代码保持紧凑,但可以保证您不会遗漏任何项目。
回答by plinth
For fun, I popped List into reflector and this is the resulting C#:
为了好玩,我将 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]);
}
}
Similarly, the MoveNext in Enumerator which is what is used by foreach is this:
类似地,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;
}
The List.ForEach is much more trimmed down than MoveNext - far less processing - will more likely JIT into something efficient..
List.ForEach 比 MoveNext 更精简 - 处理更少 - 更有可能 JIT 成为高效的东西..
In addition, foreach() will allocate a new Enumerator no matter what. The GC is your friend, but if you're doing the same foreach repeatedly, this will make more throwaway objects, as opposed to reusing the same delegate - BUT- this is really a fringe case. In typical usage you will see little or no difference.
此外,foreach() 无论如何都会分配一个新的 Enumerator。GC 是您的朋友,但是如果您重复执行相同的 foreach,这将产生更多的一次性对象,而不是重用相同的委托 -但是- 这确实是一个边缘情况。在典型用法中,您几乎看不到差异。
回答by Anthony
We had some code here (in VS2005 and C#2.0) where the previous engineers went out of their way to use list.ForEach( delegate(item) { foo;});
instead of foreach(item in list) {foo; };
for all the code that they wrote. e.g. a block of code for reading rows from a dataReader.
我们这里有一些代码(在 VS2005 和 C#2.0 中),以前的工程师特意使用这些代码list.ForEach( delegate(item) { foo;});
而不是foreach(item in list) {foo; };
他们编写的所有代码。例如,用于从 dataReader 读取行的代码块。
I still don't know exactly why they did this.
我仍然不知道他们为什么这样做。
The drawbacks of list.ForEach()
are:
的缺点list.ForEach()
是:
It is more verbose in C# 2.0. However, in C# 3 onwards, you can use the "
=>
" syntax to make some nicely terse expressions.It is less familiar. People who have to maintain this code will wonder why you did it that way. It took me awhile to decide that there wasn't any reason, except maybe to make the writer seem clever (the quality of the rest of the code undermined that). It was also less readable, with the "
})
" at the end of the delegate code block.See also Bill Wagner's book "Effective C#: 50 Specific Ways to Improve Your C#" where he talks about why foreach is preferred to other loops like for or while loops - the main point is that you are letting the compiler decide the best way to construct the loop. If a future version of the compiler manages to use a faster technique, then you will get this for free by using foreach and rebuilding, rather than changing your code.
a
foreach(item in list)
construct allows you to usebreak
orcontinue
if you need to exit the iteration or the loop. But you cannot alter the list inside a foreach loop.
它在 C# 2.0 中更加冗长。但是,从 C# 3 开始,您可以使用 "
=>
" 语法来制作一些非常简洁的表达式。它不太熟悉。必须维护此代码的人会想知道为什么要这样做。我花了一段时间才决定没有任何理由,除了可能让作者看起来很聪明(其余代码的质量破坏了这一点)。它的可读性也较差,
})
在委托代码块的末尾带有“ ”。另请参阅比尔·瓦格纳 (Bill Wagner) 的书“有效的 C#:改进 C# 的 50 种特定方法”,其中他谈到了为什么 foreach 比其他循环(如 for 或 while 循环)更受欢迎 - 重点是您让编译器决定构造的最佳方式循环。如果编译器的未来版本设法使用更快的技术,那么您将通过使用 foreach 和重建免费获得它,而不是更改您的代码。
一个
foreach(item in list)
构造允许您使用break
或continue
如果您需要退出迭代或循环。但是您不能更改 foreach 循环内的列表。
I'm surprised to see that list.ForEach
is slightly faster. But that's probably not a valid reason to use it throughout , that would be premature optimisation. If your application uses a database or web service that, not loop control, is almost always going to be be where the time goes. And have you benchmarked it against a for
loop too? The list.ForEach
could be faster due to using that internally and a for
loop without the wrapper would be even faster.
我很惊讶地发现它list.ForEach
稍微快了一点。但这可能不是始终使用它的正当理由,这将是过早的优化。如果您的应用程序使用的不是循环控制的数据库或 Web 服务,则几乎总是随时间流逝。您是否也针对for
循环对其进行了基准测试?将list.ForEach
可能会更快,由于使用内部和for
没有包装循环甚至会更快。
I disagree that the list.ForEach(delegate)
version is "more functional" in any significant way. It does pass a function to a function, but there's no big difference in the outcome or program organisation.
我不同意该list.ForEach(delegate)
版本在任何重要方面都“更具功能性”。它确实将函数传递给函数,但结果或程序组织没有太大区别。
I don't think that foreach(item in list)
"says exactly how you want it done" - a for(int 1 = 0; i < count; i++)
loop does that, a foreach
loop leaves the choice of control up to the compiler.
我不认为foreach(item in list)
“确切地说明了您希望它如何完成” -for(int 1 = 0; i < count; i++)
循环会做到这一点,foreach
循环将控制的选择留给编译器。
My feeling is, on a new project, to use foreach(item in list)
for most loops in order to adhere to the common usage and for readability, and use list.Foreach()
only for short blocks, when you can do something more elegantly or compactly with the C# 3 "=>
" operator. In cases like that, there may already be a LINQ extension method that is more specific than ForEach()
. See if Where()
, Select()
, Any()
, All()
, Max()
or one of the many other LINQ methods doesn't already do what you want from the loop.
我的感觉是,在一个新项目中,使用foreach(item in list)
大多数循环以遵守常见用法和可读性,并且list.Foreach()
仅用于短块,当您可以使用 C# 3 " =>
" 运算符执行更优雅或更紧凑的操作时。在这种情况下,可能已经存在比ForEach()
. 看看Where()
,Select()
,Any()
,All()
,Max()
或许多其他LINQ方法中的一种尚不你从循环想要的东西。
回答by Daniel Earwicker
I know two obscure-ish things that make them different. Go me!
我知道两个使它们不同的晦涩的东西。走我!
Firstly, there's the classic bug of making a delegate for each item in the list. If you use the foreach keyword, all your delegates can end up referring to the last item of the list:
首先,存在为列表中的每个项目制作委托的经典错误。如果您使用 foreach 关键字,您的所有委托最终都可以引用列表的最后一项:
// A list of actions to execute later
List<Action> actions = new List<Action>();
// Numbers 0 to 9
List<int> 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();
The List.ForEach method doesn't have this problem. The current item of the iteration is passed by value as an argument to the outer lambda, and then the inner lambda correctly captures that argument in its own closure. Problem solved.
List.ForEach 方法没有这个问题。迭代的当前项通过值作为参数传递给外部 lambda,然后内部 lambda 正确地在其自己的闭包中捕获该参数。问题解决了。
(Sadly I believe ForEach is a member of List, rather than an extension method, though it's easy to define it yourself so you have this facility on any enumerable type.)
(遗憾的是,我相信 ForEach 是 List 的成员,而不是扩展方法,尽管您自己定义它很容易,因此您可以在任何可枚举类型上使用此功能。)
Secondly, the ForEach method approach has a limitation. If you are implementing IEnumerable by using yield return, you can't do a yield return inside the lambda. So looping through the items in a collection in order to yield return things is not possible by this method. You'll have to use the foreach keyword and work around the closure problem by manually making a copy of the current loop value inside the loop.
其次,ForEach 方法有一个局限性。如果您通过使用 yield return 实现 IEnumerable,则无法在 lambda 内部执行 yield return。因此,这种方法不可能循环遍历集合中的项目以产生返回的东西。您必须使用 foreach 关键字并通过在循环内手动制作当前循环值的副本来解决闭包问题。