题
我有一个 Enumerable<T>
和我在寻找一种方法,允许我执行一项行动的每个元素,有点像 Select
但随后的副作用。是这样的:
string[] Names = ...;
Names.each(s => Console.Writeline(s));
或
Names.each(s => GenHTMLOutput(s));
// (where GenHTMLOutput cannot for some reason receive the enumerable itself as a parameter)
我没有尝试 Select(s=> { Console.WriteLine(s); return s; })
, 但这不是印刷任何东西。
解决方案
一个快速和简单的方法来是这样的:
Names.ToList().ForEach(e => ...);
其他提示
您正在寻找不断难以捉摸的ForEach,目前只存在于名单泛型集合。网上有关于微软是否应该或不应该添加为LINQ方法很多讨论。目前,你必须推出自己的:
public static void ForEach<T>(this IEnumerable<T> value, Action<T> action)
{
foreach (T item in value)
{
action(item);
}
}
虽然All()
方法提供了类似的能力,这是用例是用于在每一个项目进行测试谓词,而不是一个动作。当然,它可以被说服执行其他任务,但这个有些改变的语义和将使它更难他人解读您的代码(即是这样的使用All()
的用于测试谓词或动作?)。
免责声明:此后不再有类似于我原来的答案,而是纳入了一些七年的经验,我已经获得了。我做了编辑,因为这是一个高度看问题,并没有任何现有的答案真的涵盖所有的角度。如果你想看到我原来的答案,它是可以的 修订历史为这个职位.
首先要明白这里是C#皇宫的操作的喜欢 Select()
, All()
, Where()
, 等等,其根源在 编程功能.这个想法带来的一些更有用和易用的部件的编程功能。净的世界。这是重要的,因为一个关键宗旨的编程功能是对于操作,以免的副作用。很难低估了这一点。然而,在这种情况下的 ForEach()
/each()
, 副作用是整个puprose的操作。加入 each()
或 ForEach()
不是只是外的编程功能范围的皇宫的其他运营商,但是 直接反对他们。
但我了解这感到满意。你有一个真正的问题需要解决。为什么所有的这个象牙塔哲学的方式获得的东西,可能是真正有用吗?
埃里克利珀特,是谁在C#设计团队的时候,可以帮助我们在这里。 他建议使用一个传统 foreach
循环:
[ForEach()]增加了零新的代表性权力的语言。这样做可以让你重写这完全清除代码:
foreach(Foo foo in foos){ statement involving foo; }
进入这个代码:
foos.ForEach(foo=>{ statement involving foo; });
他的观点是,当你仔细看看你的语法选项,则不获得任何新的东西从 ForEach()
扩展与传统 foreach
循环。我部分不同意。想象一下,你有这样的:
foreach(var item in Some.Long(and => possibly)
.Complicated(set => ofLINQ)
.Expression(to => evaluate)
{
// now do something
}
这个代码混淆的意义,因为它分离 foreach
关键字从行动的循环。它还列出了环命令 之前 来操作,确定顺序上的循环的运作。这感觉更自然的想到有这些行动是第一位的,然后有的环命令 结束 查询的定义。此外,代码只是丑陋的.这似乎是它会好得多,能够编写这样的:
Some.Long(and => possibly)
.Complicated(set => ofLINQ)
.Expression(to => evaluate)
.ForEach(item =>
{
// now do something
});
然而,即使在这里,我终于来到埃里克的观点。我意识到代码就像你看到上面打了一个额外的可变的。如果你有一个复杂的组皇宫的表情像这样,你可以增添宝贵的信息来你的代码按第一分配的结果皇宫达到一个新的变量:
var queryForSomeThing = Some.Long(and => possibly)
.Complicated(set => ofLINQ)
.Expressions(to => evaluate);
foreach(var item in queryForSomeThing)
{
// now do something
}
这个代码的感觉更自然的。它把 foreach
关键词下的其余部分的循环,并查询后定义。最重要的变量名称可以添加新的信息,这将有助于未来的程序员试图了解的目的在皇宫查询。我们再次看到希望的 ForEach()
操作者真的添加没有新的表现力的语言。
然而,我们仍然失踪的两个特征的一个假设 ForEach()
扩展的方法:
- 它不是组合.我不能增加进一步的
.Where()
或GroupBy()
或OrderBy()
后一个foreach
环联的其余部分代码,而不创建一个新的声明。 - 这不是懒惰。这些行动会立即发生。它不允许我说,有一种形式,其中一个用户选择的操作的一个领域在较大的屏幕是不采取行动,直到用户按命令按钮。这种形式可能允许用户改变他们的想法之前执行该命令。这是完全正常的(容易的 即使)有一个皇宫的查询,但不作简单
foreach
.
你当然可以,让你自己 ForEach()
扩展的方法。几个其他的答案已经实施这种方法已经;它不是所有复杂。然而,我觉得这是不必要的。还有的已经一个现有的方法,该方法适合我们想要做什么从语义和运作的立场。两个失踪功能上可以讨论通过利用现有的 Select()
操作员。
Select()
符合这种转变或突出说明通过这两个上面的例子。请记住,不过,我仍将避免产生副作用。呼叫 Select()
应该返回新的对象或预测的原件。这有时可以帮助通过使用匿名方式或动态对象(如果并且只有如果必要)。如果你需要的结果持续存在,比方说,一个原始名单可变的,你可以随时打电话 .ToList()
并将其分配回到你原来的变量。我将在这里补充,我喜欢的工作 IEnumerable<T>
变量尽可能过更多具体类型。
myList = myList.Select(item => new SomeType(item.value1, item.value2 *4)).ToList();
在摘要:
- 只需坚持
foreach
大部分时间。 - 时
foreach
真的 不会做(其可能不是作为常,因为你认为),使用Select()
- 当你需要使用
Select()
, 你仍然可以避免(节目可见的)副作用,可能是通过突出的一个匿名类型。
不幸的是没有内置的方式在LINQ的当前版本中做到这一点。忽略了框架团队添加.ForEach扩展方法。有一个关于这个事情,现在在以下博客了很好的讨论。
http://blogs.msdn.com/kirillosenkov/存档/ 2009/03/31 / foreach.aspx
这是相当容易,虽然添加一个。
public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action) {
foreach ( var cur in enumerable ) {
action(cur);
}
}
您不能LINQ和做马上的IEnumerable - 你需要可以实现自己的扩展方法,或投你的枚举与LINQ数组,然后调用Array.ForEach():
Array.ForEach(MyCollection.ToArray(), x => x.YourMethod());
请注意,因为这样的值类型和结构工作,如果集合是值类型和修改集合中的元素这样一来,就会对原来的集合中的元素没有影响。
由于LINQ被设计成一个查询特征,而不是更新功能也不会觉得其执行上IEnumerable的方法的扩展
的foreach(在名称字符串名称),点击 Console.WriteLine(名称);
好了,你也可以使用标准的foreach
关键字,只是格式化成oneliner:
foreach(var n in Names.Where(blahblah)) DoStuff(n);
对不起,以为该选项理应是这里:)
有是一个foreach方法关闭列表。你可以转换成可枚举列出调用.ToList()方法,然后调用的ForEach方法关闭这一点。
另外,我听说过的人定义自己的ForEach方法关闭了IEnumerable的。这可以通过本质上调用的ForEach方法,而是在一个扩展方法包裹它来完成:
public static class IEnumerableExtensions
{
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> _this, Action<T> del)
{
List<T> list = _this.ToList();
list.ForEach(del);
return list;
}
}
使用并行LINQ:
Names.AsParallel()。的ForAll(名称=> ...)