Possible Duplicate:
LINQ equivalent of foreach for IEnumerable
List有一个名为ForEach的方法,它对它的每个元素执行传递的操作。
1 2 3
| var names = new List <String>{"Bruce", "Alfred", "Tim", "Richard" };
names .ForEach(p => { Console .WriteLine(p ); }); |
但是如果names不是List而是IList的话,IList就没有ForEach这样的方法了。还有别的选择吗?
- 为什么不能使用foreach循环?
- 这被认为是个坏主意,你到底得到了什么?代码不短…-blogs.msdn.com/b/mazhou/archive/2011/09/21/&hellip;
- 您可以只添加foreach作为ilist的扩展方法。
- @别这样,永远不要。
- @虽然我自己不会这么做,但博客中的所有原因都是…漂亮垃圾。
- 标记为重复,但实际上不是。要解决此问题,如果知道类型:ilistnames.oftype.foreach(s=>console.writeline(s)),则可以在ilist上使用foreach;
- 为了稍微提高可读性,实际上可以编写names.ForEach(Console.WriteLine);。
使用ForEach循环:
1 2 3
| foreach (var p in names) {
Console.WriteLine(p);
} |
如果委托和扩展方法不能提高可读性,那么就没有理由到处使用委托和扩展方法;ForEach循环比ForEach方法更能明确地告诉读者正在做什么。
- 我不同意最后一句话。它使代码更短,并且不易出错。为什么要列出然后有一个foreach?
- @Nawfal我非常同意文章的最后一行;.ForEach方法在很大程度上是多余的;它也不那么直接。在大多数情况下,仅使用常规foreach是比较可取的。
- @Nawfal:调用foreach方法的错误率会降低多少?必须插入具有兼容签名的正确委托这一事实将是产生错误的额外机会。关于List的问题,请查看对该问题的选定答案:"由于它没有与其他扩展方法一起添加,因此可以假设c设计人员认为它是一个糟糕的设计,更喜欢foreach构造。"同时,第二个答案也是如此。
- @Marcgravell不知道为什么不那么直接。在我看来,每次编写foreach循环都比在某个地方编写一次更糟糕,而且方法名也不那么模糊,它只是说明这是在做什么:foreach循环和集合项上的操作。
- @纳法尔:你在考虑一个特殊的案例,比如names.ForEach(Console.WriteLine);,对吧?只有这样,foreach方法才不那么直接,但在循环体中只有一个方法调用可能不是通常的情况。
- @O.R.Mapper我刚刚说明了一个常见的事实,即在某个地方编写函数结构以供重用,这样可以减少进一步调用的混乱和错误发生率(就像Linq所做的那样)。我同意这是一个很小的案例,它不太容易出错。
- @o.r.mapper关于这个链接,blogs.msdn.com/b/mazhou/archive/2011/09/21/&hellip;那个PPL经常引用的话,这些观点对我来说并不是很有说服力,因为它对很多读者来说都是如此。
- @Nawfal:但这正是问题所在:上面的foreach循环并不比像names.ForEach(p => Console.WriteLine(p));这样的调用更混乱(而且一旦执行多个命令,情况会更糟),并且它不会通过引入额外的方法调用来添加额外的间接层(我认为这会使foreach方法更容易出错)。最重要的是,foreach方法并不是一个函数构造,这正是因为我在前面提到的问题中提到的副作用。
- list.forEach实际上不是一个特别好的设计。一方面,它实际上转换成了一个"for"循环,而不是foreach循环(我认为设计师试图从性能上获得更多的好处)。这意味着您传递给list的"操作".forEach实际上可以修改列表本身而不必抱怨!!!!这可能是一个令人讨厌的错误来源……
- @amitmittal在List上有许多修改集合的方法,还有很多不修改集合的方法(如linq方法)。点就是你画线的地方。
如果你的IList是一个数组(T[]),那么你在它们上面有array.foreach方法,类似于List上的ForEach。您可以为您的自定义IList或IEnumerable或您喜欢的任何内容创建扩展方法。
1 2 3 4 5
| public static void ForEach<T>(this IList<T> list, Action<T> action)
{
foreach (T t in list)
action(t);
} |
您只需注意这样一个事实:原始集合中的对象将被修改,但我猜命名确实意味着这一点。
——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
我更喜欢打电话:
1 2 3
| people.Where(p => p.Tenure > 5)
.Select(p => p.Nationality)
.ForEach(n => AssignCitizenShip(n); |
比
1 2 3 4
| foreach (var n in people.Where(p => p.Tenure > 5).Select(p => p.Nationality))
{
AssignCitizenShip(n);
} |
如果是这样,您可以在EDCOX1 OR 6中创建扩展方法。请注意,终止调用EDCOX1(2)执行EDOCX1×8查询。如果您不需要,也可以使用yield语句并返回IEnumerable来延迟它:
1 2 3 4 5 6 7 8
| public static IEnumerable<T> ForEach<T>(this IEnumerable<T> list, Action<T> action)
{
foreach (T t in list)
{
action(t);
yield return t;
}
} |
这解决了副作用问题,但我个人喜欢一个名为ForEach的方法来最终执行调用。
——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————---
为了解决对偏好的对立观点,埃里克·利珀特提供了一个比这更好的链接。引用他:
"The first reason is that doing so violates the functional programming
principles that all the other sequence operators are based upon.
Clearly the sole purpose of a call to this method is to cause side
effects. The purpose of an expression is to compute a value, not to
cause a side effect. The purpose of a statement is to cause a side
effect. The call site of this thing would look an awful lot like an
expression (though, admittedly, since the method is void-returning,
the expression could only be used in a"statement expression"
context.) It does not sit well with me to make the one and only
sequence operator that is only useful for its side effects.
The second reason is that doing so adds zero new representational
power to the language".
埃里克并没有说这是一件糟糕的事情——只是出于哲学上的原因,他决定不在默认情况下将构造包含在Linq中。如果您认为IEnumerable上的函数不应该作用于内容,那么就不要这样做。我个人并不介意,因为我很清楚它的作用。我将它视为对集合类产生副作用的任何其他方法。如果需要的话,我可以进入函数并调试它。这是另一个来自Linq本身的。
1 2 3 4
| people.Where(p => p.Tenure > 5)
.Select(p => p.Nationality)
.AsParallel()
.ForAll(n => AssignCitizenShip(n); |
正如我想说的,这些没有什么不好的。这只是个人喜好。我不会在嵌套的ForEach中使用它,或者如果它涉及多行代码在ForEach循环中执行,因为这显然是不可读的。但举个简单的例子,我喜欢它。看起来简洁明了。
编辑:请参见性能链接btw:Why is list.foreach speed than standard foreach?
- 为什么要投票??
- 也许是因为你把我的答案复制成了你的答案(比我的答案大一分钟)
- @virtualvoid这是一个错误的声明;停留在显示的时间上;您的列表是2012-12-19 06:35:45Z,nawfal的列表是2012-12-19 06:35:08Z。他的年纪真大。
- @virtualvoid即使您不知道时间,如何从新创建的答案(如您所说)中复制旧的答案???
- 因为一开始,我把答案作为评论添加——在你之前,在那之后,我删除了它并作为常规答案发布。
- 我反对投反对票的冲动,因为你写了"foreah"…P
- 虚幻需要在几秒钟内从你的评论中发表答案。尽管如此,任何人都知道这是一件微不足道的工作。所有支持我们的人都不是出于敬畏,而是为了支持他们的观点,也许…P
- @ Shaamaan我纠正了,而且更多:P
- @纳瓦尔的回答很有帮助,我喜欢简单易行的方式作为O.R.Mapper帖子。但有时我会尝试语法糖。我已经否决了你的回答和评论。
您可以创建一个扩展方法并使用void List.ForEach(Action action)的大部分实现。您可以在共享源代码计划站点下载源代码。
基本上你会结束这样的事情:
1 2 3 4 5 6 7 8 9 10
| public static void ForEach<T >(this IList <T > list, Action <T > action )
{
if (list == null) throw new ArgumentNullException ("null");
if (action == null) throw new ArgumentNullException ("action");
for (int i = 0; i < list .Count; i ++)
{
action (list [i ]);
}
} |
它比使用ForEach语句的其他实现稍好,因为它利用了这样一个事实,即ilist包含索引器。
虽然我赞同O.R.Mapper的回答,但有时在许多开发人员参与的大型项目中,很难让每个人都相信ForEach的声明更清晰。更糟糕的是,如果您的API是基于接口(ilist)而不是具体类型(list),那么用于List.ForEach method的开发人员可能会开始在您的ilist引用上调用ToList!我知道,因为它发生在我以前的项目中。我按照框架设计指南在公共API中的任何地方使用了集合接口。我花了一段时间才注意到,许多不习惯这种做法的开发人员开始以惊人的速度逮捕ToList。最后,我将这个扩展方法添加到每个人都在使用的公共程序集中,并确保从代码库中删除对ToList的所有不必要的调用。
- 是的,因为list.ForEach不涉及铸造(如foreach),所以速度更快。这里有一个相关的问题stackoverflow.com/questions/744085/&hellip;(不是说避免foreach是有意义的)
将此代码添加到静态类并调用它的扩展名:
1 2 3 4 5
| public static void ForEach<T>(this IList<T> list, Action<T> action) {
foreach(var item in list) {
action.Invoke(item);
}
} |