我想在LINQ中执行以下等效操作,但我无法确定如何执行:

IEnumerable<Item> items = GetItems();
items.ForEach(i => i.DoStuff());

真正的语法是什么?


当前回答

您可以使用FirstOrDefault()扩展,该扩展可用于IEnumerable<T>。通过从谓词返回false,它将为每个元素运行,但不会在意它实际上没有找到匹配项。这将避免ToList()开销。

IEnumerable<Item> items = GetItems();
items.FirstOrDefault(i => { i.DoStuff(); return false; });

其他回答

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);
    }
}

Fredrik提供了解决方案,但可能值得考虑一下为什么这不在框架中。我认为,LINQ查询运算符应该是无副作用的,符合合理的功能性世界观。很明显,ForEach恰恰相反——一个纯粹基于副作用的构造。

这并不是说这是一件坏事——只是想想这个决定背后的哲学原因。

很多人都提到过,但我不得不把它写下来。这不是最清晰/最可读吗?

IEnumerable<Item> items = GetItems();
foreach (var item in items) item.DoStuff();

简短而简单(st)。

我分别不同意链接扩展方法应该是无副作用的概念(不仅因为它们不是,任何委托都可以执行副作用)。

考虑以下事项:

   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);
      }
   }

该示例显示的实际上只是一种后期绑定,它允许调用对元素序列有副作用的许多可能的操作之一,而不必编写大开关构造来解码定义该操作的值并将其转换为相应的方法。

为了保持流利,可以使用以下技巧:

GetItems()
    .Select(i => new Action(i.DoStuf)))
    .Aggregate((a, b) => a + b)
    .Invoke();