受另一个询问缺少Zip
功能的问题的启发:
为什么ForEach
类中没有Enumerable
扩展方法?还是在任获得ForEach
方法的唯一类是List<>
。有没有理由错过(表演)?
答案 0 :(得分:181)
在大多数情况下,该语言中已包含foreach语句。
我不想看到以下内容:
list.ForEach( item =>
{
item.DoSomething();
} );
而不是:
foreach(Item item in list)
{
item.DoSomething();
}
后者更清晰,更容易阅读在大多数情况下,虽然输入可能要长一点。
但是,我必须承认我在这个问题上改变了立场;在某些情况下,ForEach()扩展方法确实很有用。
以下是声明与方法之间的主要区别:
这些都是很多人在这里提出的重点,我可以看出为什么人们会错过这个功能。我不介意微软在下一个框架迭代中添加标准的ForEach方法。
答案 1 :(得分:70)
在LINQ之前添加了ForEach方法。如果添加ForEach扩展,由于扩展方法约束,它将永远不会为List实例调用。我认为没有添加的原因是不干扰现有的。
但是,如果你真的错过这个小功能,你可以推出自己的版本
public static void ForEach<T>(
this IEnumerable<T> source,
Action<T> action)
{
foreach (T element in source)
action(element);
}
答案 2 :(得分:48)
您可以编写此扩展方法:
// Possibly call this "Do"
IEnumerable<T> Apply<T> (this IEnumerable<T> source, Action<T> action)
{
foreach (var e in source)
{
action(e);
yield return e;
}
}
<强>赞成强>
允许链接:
MySequence
.Apply(...)
.Apply(...)
.Apply(...);
<强>缺点强>
在你做一些强制迭代之前,它实际上不会做任何事情。因此,不应将其称为.ForEach()
。你可以在最后写.ToList()
,或者你也可以编写这个扩展方法:
// possibly call this "Realize"
IEnumerable<T> Done<T> (this IEnumerable<T> source)
{
foreach (var e in source)
{
// do nothing
;
}
return source;
}
这可能与运送C#库有太大不同;不熟悉您的扩展方法的读者将不知道如何编写代码。
答案 3 :(得分:32)
The discussion here给出答案:
实际上,我目睹的具体讨论确实取决于功能纯度。在表达式中,经常假设没有副作用。拥有ForEach是特别邀请副作用,而不仅仅是忍受它们。 - Keith Farmer(合伙人)
基本上,决定保持扩展方法在功能上“纯粹”。当使用Enumerable扩展方法时,ForEach会鼓励副作用,这不是意图。
答案 4 :(得分:16)
虽然我同意在大多数情况下使用内置foreach
构造更好,但我发现在ForEach&lt;&gt;上使用此变体。扩展比我自己在常规foreach
中管理索引要好一些:
public static int ForEach<T>(this IEnumerable<T> list, Action<int, T> action)
{
if (action == null) throw new ArgumentNullException("action");
var index = 0;
foreach (var elem in list)
action(index++, elem);
return index;
}
例
var people = new[] { "Moe", "Curly", "Larry" };
people.ForEach((i, p) => Console.WriteLine("Person #{0} is {1}", i, p));
会给你:
Person #0 is Moe
Person #1 is Curly
Person #2 is Larry
答案 5 :(得分:14)
一种解决方法是编写.ToList().ForEach(x => ...)
。
<强>优点强>
易于理解 - 读者只需知道C#附带的内容,而不是任何其他扩展方法。
句法噪音非常温和(只增加了一些极端的代码)。
通常不会花费额外的内存,因为本机.ForEach()
无论如何都必须实现整个集合。
<强>缺点强>
操作顺序并不理想。我宁愿意识到一个元素,然后采取行动,然后重复。此代码首先实现所有元素,然后依次对它们进行操作。
如果实现列表会引发异常,则永远不会对单个元素执行操作。
如果枚举是无限的(就像自然数字一样),那你就不走运了。
答案 6 :(得分:12)
我一直想知道自己,这就是为什么我总是随身携带这个:
public static void ForEach<T>(this IEnumerable<T> col, Action<T> action)
{
if (action == null)
{
throw new ArgumentNullException("action");
}
foreach (var item in col)
{
action(item);
}
}
很好的小扩展方法。
答案 7 :(得分:7)
因此,有很多关于ForEach扩展方法不适合的事实的评论,因为它不返回类似LINQ扩展方法的值。虽然这是一个事实陈述,但并非完全正确。
LINQ扩展方法都返回一个值,因此它们可以链接在一起:
collection.Where(i => i.Name = "hello").Select(i => i.FullName);
然而,仅仅因为LINQ是使用扩展方法实现的,并不意味着扩展方法必须以相同的方式使用并返回一个值。编写扩展方法以公开不返回值的常用功能是完全有效的用法。
关于ForEach的具体论点是,基于对扩展方法的约束(即扩展方法将从不覆盖具有相同签名的继承方法),可能存在这样的情况:自定义扩展方法可用于所有阻碍IEnumerable <T
&gt;的类。列表<T
&gt;除外。当方法开始表现不同时,这会导致混淆,具体取决于是否调用了扩展方法或继承方法。
答案 8 :(得分:6)
您可以使用(可链接,但延迟评估)Select
,首先执行您的操作,然后返回身份(或者您喜欢的其他内容)
IEnumerable<string> people = new List<string>(){"alica", "bob", "john", "pete"};
people.Select(p => { Console.WriteLine(p); return p; });
您需要确保仍然使用Count()
(枚举afaik的最便宜的操作)或者您需要的其他操作来评估它。
我很乐意看到它带入标准库:
static IEnumerable<T> WithLazySideEffect(this IEnumerable<T> src, Action<T> action) {
return src.Select(i => { action(i); return i; } );
}
上面的代码然后变成people.WithLazySideEffect(p => Console.WriteLine(p))
,它实际上等同于foreach,但是懒惰和可链接。
答案 9 :(得分:4)
请注意, MoreLINQ NuGet提供了您正在寻找的ForEach
扩展方法(以及执行委托并生成结果的Pipe
方法)。参见:
答案 10 :(得分:4)
@Coincoin
foreach扩展方法的真正强大功能包括Action<>
的可重用性,而无需为代码添加不必要的方法。假设您有10个列表,并且您希望对它们执行相同的逻辑,并且相应的函数不适合您的类,并且不会重复使用。您可以将所有逻辑保存在一个位置(Action<>
,而不是拥有10个for循环,或者显然是不属于的辅助函数的通用函数。所以,将数十行替换为
Action<blah,blah> f = { foo };
List1.ForEach(p => f(p))
List2.ForEach(p => f(p))
等...
逻辑在一个地方,你没有污染你的班级。
答案 11 :(得分:2)
如果你有F#(将在.NET的下一个版本中),你可以使用
Seq.iter doSomething myIEnumerable
答案 12 :(得分:2)
部分原因是语言设计者从哲学角度不同意。
https://blogs.msdn.microsoft.com/ericlippert/2009/05/18/foreach-vs-foreach/
答案 13 :(得分:2)
我想扩展Aku's answer。
如果你想调用方法的唯一目的是它的副作用而不先迭代整个可枚举,你可以使用它:
private static IEnumerable<T> ForEach<T>(IEnumerable<T> xs, Action<T> f) {
foreach (var x in xs) {
f(x); yield return x;
}
}
答案 14 :(得分:2)
在3.5中,添加到IEnumerable的所有扩展方法都支持LINQ(注意它们是在System.Linq.Enumerable类中定义的)。在这篇文章中,我解释了为什么foreach不属于LINQ: Existing LINQ extension method similar to Parallel.For?
答案 15 :(得分:2)
我写了一篇关于它的博文: http://blogs.msdn.com/kirillosenkov/archive/2009/01/31/foreach.aspx
如果您想在.NET 4.0中看到此方法,可以在这里投票: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=279093
答案 16 :(得分:2)
大多数LINQ扩展方法都返回结果。 ForEach不适合这种模式,因为它什么都不返回。
答案 17 :(得分:2)
当您想要返回某些内容时,可以使用select。 如果不这样做,则可以先使用ToList,因为您可能不想修改集合中的任何内容。
答案 18 :(得分:2)
是我还是名单&lt; T&gt; .Foreach几乎已经被Linq淘汰了。 最初有
foreach(X x in Y)
其中Y只需要IEnumerable(Pre 2.0),并实现GetEnumerator()。 如果查看生成的MSIL,您可以看到它与
完全相同IEnumerator<int> enumerator = list.GetEnumerator();
while (enumerator.MoveNext())
{
int i = enumerator.Current;
Console.WriteLine(i);
}
(请参阅http://alski.net/post/0a-for-foreach-forFirst-forLast0a-0a-.aspx了解MSIL)
然后在DotNet2.0中出现了Generics和List。 Foreach一直觉得我是Vistor模式的实现,(参见Gamma,Helm,Johnson,Vlissides的Design Patterns)。
现在当然在3.5中我们可以使用Lambda来获得相同的效果,例如尝试 http://dotnet-developments.blogs.techtarget.com/2008/09/02/iterators-lambda-and-linq-oh-my/
答案 19 :(得分:1)
要使用Foreach,您的列表应加载到主内存中。但由于IEnumerable的延迟加载性质,它们不会在IEnumerable中提供ForEach。
但是,通过急切加载(使用ToList()),您可以将列表加载到内存中并利用ForEach。
答案 20 :(得分:0)
还没有人指出ForEach&lt; T&gt;导致编译时类型检查foreach关键字在运行时检查的位置。
在代码中使用了两种方法时进行了一些重构,我赞成.ForEach,因为我不得不寻找测试失败/运行时失败来找到foreach问题。
答案 21 :(得分:0)
我的版本是一种扩展方法,允许您在 T 的 IEnumerable 上使用 ForEach
public static class EnumerableExtension
{
public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
source.All(x =>
{
action.Invoke(x);
return true;
});
}
}