我想在LINQ中执行以下等效操作,但我无法确定如何执行:
IEnumerable<Item> items = GetItems();
items.ForEach(i => i.DoStuff());
真正的语法是什么?
我想在LINQ中执行以下等效操作,但我无法确定如何执行:
IEnumerable<Item> items = GetItems();
items.ForEach(i => i.DoStuff());
真正的语法是什么?
当前回答
IEnumerable没有ForEach扩展;仅适用于列表<T>。所以你可以
items.ToList().ForEach(i => i.DoStuff());
或者,编写自己的ForEach扩展方法:
public static void ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
{
foreach(T item in enumeration)
{
action(item);
}
}
其他回答
我分别不同意链接扩展方法应该是无副作用的概念(不仅因为它们不是,任何委托都可以执行副作用)。
考虑以下事项:
public class Element {}
public Enum ProcessType
{
This = 0, That = 1, SomethingElse = 2
}
public class Class1
{
private Dictionary<ProcessType, Action<Element>> actions =
new Dictionary<ProcessType,Action<Element>>();
public Class1()
{
actions.Add( ProcessType.This, DoThis );
actions.Add( ProcessType.That, DoThat );
actions.Add( ProcessType.SomethingElse, DoSomethingElse );
}
// Element actions:
// This example defines 3 distict actions
// that can be applied to individual elements,
// But for the sake of the argument, make
// no assumption about how many distict
// actions there may, and that there could
// possibly be many more.
public void DoThis( Element element )
{
// Do something to element
}
public void DoThat( Element element )
{
// Do something to element
}
public void DoSomethingElse( Element element )
{
// Do something to element
}
public void Apply( ProcessType processType, IEnumerable<Element> elements )
{
Action<Element> action = null;
if( ! actions.TryGetValue( processType, out action ) )
throw new ArgumentException("processType");
foreach( element in elements )
action(element);
}
}
该示例显示的实际上只是一种后期绑定,它允许调用对元素序列有副作用的许多可能的操作之一,而不必编写大开关构造来解码定义该操作的值并将其转换为相应的方法。
这种“功能方法”抽象泄露了大量时间。在语言层面上没有任何东西可以防止副作用。只要你能让它为容器中的每个元素调用lambda/委托,你就会得到“ForEach”行为。
例如,这里有一种将srcDictionary合并到destDictionary的方法(如果键已经存在-重写)
这是一个黑客,不应该在任何生产代码中使用。
var b = srcDictionary.Select(
x=>
{
destDictionary[x.Key] = x.Value;
return true;
}
).Count();
ForEach的目的是造成副作用。IEnumerable用于集合的惰性枚举。
当你考虑到这一点时,这个概念上的差异是非常明显的。
SomeEnumerable.ForEach(item=>DataStore.Synchronize(item));
在您对其执行“计数”或“ToList()”或其他操作之前,这不会执行。这显然不是所表达的。
您应该使用IEnumerable扩展来设置迭代链,根据其各自的源和条件定义内容。表达树是强大而高效的,但你应该学会欣赏它们的本质。而且不仅仅是为了围绕它们进行编程,以节省几个字符而忽略惰性求值。
正如许多答案已经指出的那样,您可以自己轻松地添加这样的扩展方法。然而,如果您不想这样做,尽管我不知道BCL中有什么类似的内容,但如果您已经引用了Reactive Extension(如果没有,您应该有),System命名空间中仍然有一个选项:
using System.Reactive.Linq;
items.ToObservable().Subscribe(i => i.DoStuff());
虽然方法名称有点不同,但最终结果正是您所希望的。
MoreLinq有IEnumerable<T>.ForEach和许多其他有用的扩展。仅为ForEach使用依赖关系可能不值得,但其中有很多有用的东西。
https://www.nuget.org/packages/morelinq/
https://github.com/morelinq/MoreLINQ