提问人:Cameron MacFarland 提问时间:9/19/2008 最后编辑:SergeyCameron MacFarland 更新时间:8/5/2022 访问量:198992
为什么 IEnumerable 上没有 ForEach 扩展方法?
Why is there no ForEach extension method on IEnumerable?
问:
灵感来自另一个关于缺失功能的问题:Zip
为什么接口上没有扩展方法?还是在任何地方?唯一获得方法的类是 。它缺失是有原因的,也许是性能?ForEach
IEnumerable
ForEach
List<>
答:
大多数 LINQ 扩展方法都返回结果。ForEach 不适合此模式,因为它不返回任何内容。
评论
public IEnumerable<T> Foreach<T>(this IEnumerable<T> items, Action<T> action) { /* error check */ foreach (var item in items) { action(item); yield return item; } }
ForEach 方法已添加到 LINQ 之前。如果添加 ForEach 扩展,则由于扩展方法约束,永远不会为 List 实例调用它。我认为没有添加它的原因是不干扰现有的。
但是,如果您真的错过了这个不错的小功能,则可以推出自己的版本
public static void ForEach<T>(
this IEnumerable<T> source,
Action<T> action)
{
foreach (T element in source)
action(element);
}
评论
我自己也一直很想,这就是为什么我总是随身携带这个:
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);
}
}
不错的小扩展方法。
评论
语言中已经包含了一个语句,该语句在大多数时候都完成了这项工作。foreach
我讨厌看到以下内容:
list.ForEach( item =>
{
item.DoSomething();
} );
而不是:
foreach(Item item in list)
{
item.DoSomething();
}
在大多数情况下,后者更清晰、更易于阅读,尽管打字时间可能更长一些。
但是,我必须承认,我在这个问题上改变了立场;在某些情况下,扩展方法确实很有用。ForEach()
以下是语句和方法之间的主要区别:
- 类型检查:foreach 在运行时完成,在编译时完成(大加!
ForEach()
- 调用委托的语法确实要简单得多:objects。ForEach(做某事);
- ForEach() 可以被链接:尽管这种功能的邪恶/有用性是可以讨论的。
这些都是这里许多人提出的好观点,我明白为什么人们错过了这个功能。我不介意 Microsoft 在下一次框架迭代中添加标准的 ForEach 方法。
评论
foreach
ForEach
col.Where(...).OrderBy(...).ForEach(...)
list.ForEach(item => item.DoSomething())
in
如果你有 F#(将在下一版本的 .NET 中),则可以使用
Seq.iter doSomething myIEnumerable
评论
是我还是List<T>。Foreach 几乎被 Linq 淘汰了。 最初有
foreach(X x in Y)
其中 Y 只需是 IEnumerable(2.0 之前),并实现 GetEnumerator()。 如果查看生成的 MSIL,您可以看到它与
IEnumerator<int> enumerator = list.GetEnumerator();
while (enumerator.MoveNext())
{
int i = enumerator.Current;
Console.WriteLine(i);
}
(请参阅 MSIL 的 http://alski.net/post/0a-for-foreach-forFirst-forLast0a-0a-.aspx)
然后在 DotNet2.0 中出现了泛型和列表。在我看来,Foreach 一直是 Vistor 模式的实现,(参见 Gamma、Helm、Johnson、Vlissides 的设计模式)。
当然,在 3.5 中,我们可以改用 Lambda 来达到相同的效果,例如尝试 http://dotnet-developments.blogs.techtarget.com/2008/09/02/iterators-lambda-and-linq-oh-my/
评论
因此,有很多评论认为 ForEach 扩展方法不合适,因为它不像 LINQ 扩展方法那样返回值。虽然这是一个事实陈述,但并不完全正确。
LINQ 扩展方法都返回一个值,以便它们可以链接在一起:
collection.Where(i => i.Name = "hello").Select(i => i.FullName);
但是,仅仅因为 LINQ 是使用扩展方法实现的,并不意味着必须以相同的方式使用扩展方法并返回值。编写扩展方法来公开不返回值的常见功能是完全有效的用法。
关于 ForEach 的具体论点是,基于对扩展方法的约束(即扩展方法永远不会重写具有相同签名的继承方法),可能会出现这样一种情况,即自定义扩展方法在隐含 IEnumerable 的所有类上都可用>但 List> 除外。当方法开始根据是否调用扩展方法或继承方法而表现出不同的行为时,这可能会导致混淆。<T
<T
当您想要返回某些内容时,可以使用 select。 如果不这样做,可以先使用 ToList,因为您可能不想修改集合中的任何内容。
评论
虽然我同意在大多数情况下使用内置结构更好,但我发现在 ForEach<> 扩展上使用这种变体比自己在常规中管理索引要好一些:foreach
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
评论
这里的讨论给出了答案:
实际上,我目睹的具体讨论实际上取决于功能纯度。在表达式中,经常假设没有副作用。拥有 ForEach 特别会带来副作用,而不仅仅是忍受它们。-- Keith Farmer (合伙人)
基本上,决定保持扩展方法在功能上的“纯粹”。在使用 Enumerable 扩展方法时,ForEach 会鼓励副作用,这不是本意。
评论
一种解决方法是编写 ..ToList().ForEach(x => ...)
优点
易于理解 - 读者只需要知道 C# 附带了什么,而不需要任何其他扩展方法。
语法噪声非常温和(只添加了一些无关的代码)。
通常不会花费额外的内存,因为无论如何,本地人都必须实现整个集合。.ForEach()
缺点
操作顺序并不理想。我宁愿意识到一个元素,然后采取行动,然后重复。此代码首先实现所有元素,然后按顺序对它们执行操作。
如果意识到列表引发了异常,则永远无法对单个元素执行操作。
如果枚举是无限的(如自然数),那么你就不走运了。
评论
Enumerable.ForEach<T>
List<T>.ForEach
public static void ForEach<T>(this IEnumerable<T> list, Action<T> action) { foreach (T item in list) action(item); }
你可以写这个扩展方法:
// 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# 库有太大的区别;不熟悉扩展方法的读者将不知道该如何编写代码。
评论
{foreach (var e in source) {action(e);} return source;}
numbers.Select(n => n*2);
Select()
Apply()
Action
Select(e => { action(e); return e; })
@Coincoin
foreach 扩展方法的真正强大之处在于 的可重用性,而无需向代码添加不必要的方法。假设你有 10 个列表,并且你想对它们执行相同的逻辑,并且相应的函数不适合你的类,并且不会被重用。您可以将所有逻辑保存在一个地方(.因此,数十行被替换为Action<>
Action<>
Action<blah,blah> f = { foo };
List1.ForEach(p => f(p))
List2.ForEach(p => f(p))
等。。。
逻辑在一个地方,你没有污染你的班级。
评论
f(p)
{ }
for (var p in List1.Concat(List2).Concat(List2)) f(p);
目前还没有人指出 ForEach<T> 会导致编译时类型检查,其中 foreach 关键字在运行时进行检查。
在代码中使用了这两种方法的地方进行了一些重构后,我赞成.ForEach,因为我必须寻找测试失败/运行时失败才能找到 foreach 问题。
评论
foreach
object
ForEach
我写了一篇关于它的博客文章:http://blogs.msdn.com/kirillosenkov/archive/2009/01/31/foreach.aspx
如果你想在 .NET 4.0 中看到此方法,可以在此处投票: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=279093
评论
在 3.5 中,添加到 IEnumerable 的所有扩展方法都用于 LINQ 支持(请注意,它们是在 System.Linq.Enumerable 类中定义的)。在这篇文章中,我解释了为什么 foreach 不属于 LINQ:现有的 LINQ 扩展方法类似于 Parallel.For?
您可以使用 (chainable, but lazyed evaluate ),首先执行操作,然后返回身份(或者如果您愿意,可以返回其他内容)Select
IEnumerable<string> people = new List<string>(){"alica", "bob", "john", "pete"};
people.Select(p => { Console.WriteLine(p); return p; });
您需要确保它仍然被评估,无论是使用(枚举 afaik 的最便宜的操作)还是您无论如何都需要的其他操作。Count()
不过,我很想看到它被引入标准库:
static IEnumerable<T> WithLazySideEffect(this IEnumerable<T> src, Action<T> action) {
return src.Select(i => { action(i); return i; } );
}
然后,上面的代码实际上等同于 foreach,但懒惰且可链接。people.WithLazySideEffect(p => Console.WriteLine(p))
评论
Select
Select
Select
Select
Select
请注意,MoreLINQ NuGet 提供了你要查找的扩展方法(以及执行委托并生成其结果的方法)。看:ForEach
Pipe
我想扩展一下阿库的回答。
如果你想调用一个方法的唯一目的,而不是先迭代整个枚举,你可以使用这个:
private static IEnumerable<T> ForEach<T>(IEnumerable<T> xs, Action<T> f) {
foreach (var x in xs) {
f(x); yield return x;
}
}
评论
Select(x => { f(x); return x;})
部分原因是语言设计者从哲学的角度不同意它。
- 没有(和测试...)一个功能比有一个功能要少得多。
- 它并不是很短(有一些传递函数的情况,但这不是主要用途)。
- 它的目的是产生副作用,这不是 linq 的意义所在。
- 为什么有另一种方法可以做与我们已经拥有的功能相同的事情?(foreach 关键字)
https://blogs.msdn.microsoft.com/ericlippert/2009/05/18/foreach-vs-foreach/
我的版本是一个扩展方法,允许您在 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;
});
}
}
评论
Parallel.ForEach
Enumerable.ForEach