.Net可执行扩展,执行Action并返回相同的枚举

时间:2014-03-05 14:30:50

标签: c# .net linq extension-methods ienumerable

有没有办法使用IEnumerable扩展程序Select,(或任何其他IEnumerable扩展程序) 执行相当于返回枚举的ForEach()?即,您是否已经使用内置提供的IEnumerable扩展程序,这相当于此自定义扩展程序?

  public static class EnumerableExtensions
  {
      public static IEnumerable<T> Do<T>
          (this IEnumerable<T> source, Action<T> action)
      {
         foreach (var elem in source)
         {
            action(elem);
            yield return elem;
         }
     }
 }

对于下面的评论,此方法与调用foreach不完全相同。在foreach的情况下,不返回枚举。在这种情况下,构造的目的仅仅是执行动作,因此很难称为副作用。

它允许链接动作,如

var Invoices = GetUnProcessedInvoices();
Invoices.Where(i=>i.Date > someDate)
      .Do(i=>SendEmail(i.Customer))
      .Do(i=>ShipProduct(i.Customer, i.Product, i.Quamtity))
      .Do(i=>Inventory.Adjust(i.Product, i.Quantity));

5 个答案:

答案 0 :(得分:4)

您可以使用:

var result = source.Select(x => { action(x); return x; });

但是当然懒惰的评估意味着在评估结果之前它不会被执行:

var result = source.Select(x => { action(x); return x; });
var list = result.ToList(); // action executed here for each element

如果枚举未完全枚举,则不会为所有元素调用操作:

var result = source.Select(x => { action(x); return x; });
var first = result.First(); // action executed here only for first element

如果用户多次枚举,您的动作将被多次枚举,如下面的设计示例所示:

var result = source.Select(x => { action(x); return x; });
if (result.Count() > 0) // Count() enumerates and calls action() for each element
{
    return result.ToList(); // ToList() enumerates and calls action() again for each element.
}
else
{
    return null;
}

恕我直言,依靠你的枚举用户来确保你的动作只被调用一次可能会令人困惑,所以我通常会避免这种设计。

答案 1 :(得分:2)

Microsoft发布了NuGet包Ix-Main,其中包含各种有用的扩展程序,如Do()

你的例子:

var Invoices = GetUnProcessedInvoices();
Invoices.Where(i=>i.Date > someDate)
      .Do(i=>SendEmail(i.Customer))
      .Do(i=>ShipProduct(i.Customer, i.Product, i.Quamtity))
      .Do(i=>Inventory.Adjust(i.Product, i.Quantity));

开箱即用。

答案 2 :(得分:0)

您的方法的等价物只是使用选择:

var x = list.Select(x => { x.Something(); return x; });

答案 3 :(得分:0)

您可以在多个LINQ扩展方法中添加副作用,例如WhereSelect

var en = enumeration.Where(x => { action(x); return true; });

或:

var en = enumeration.Select(x => { action(x); return x; });

如果您的操作返回调用它的值,您将获得最短的代码,然后您可以在扩展方法中将该方法用作委托:

var en = enumeration.Select(action);

或者总是返回true

var en = enumeration.Where(action);

要实际调用该方法,您还必须使用实际枚举该集合的内容,例如Last()方法:

en.Last();

答案 4 :(得分:0)

好吧,我会将我的评论添加为答案。我想如果你想要一个代码库来使用链接,那么代码库应该真的支持它。否则,每个开发人员可能会或可能不会以您想要的功能样式编写。所以,我会这样做:

  public static class EnumerableExtensions {

    public static IEnumerable<Invoice> SendEmail(this IEnumerable<Invoice> invoices) {
        foreach (var invoice in invoices) {
            SendEmail(invoice.Customer);
            yield return invoice;
        }
    }        

    public static IEnumerable<Invoice> ShipProduct(this IEnumerable<Invoice> invoices) {
        foreach (var invoice in invoices) {
            ShipProduct(invoice.Customer, invoice.Product, invoice.Quantity);
            yield return invoice;
        }
    }                     

    public static IEnumerable<Invoice> AdjustInventory(this IEnumerable<Invoice> invoices) {
        foreach (var invoice in invoices) {
            AdjustInventory(invoice.Product, invoice.Quantity);
            yield return invoice;
        }
    }


    private static void SendEmail(object p) {
        Console.WriteLine("Email!");
    }
    private static void AdjustInventory(object p1, object p2) {
        Console.WriteLine("Adjust inventory!");
    }
    private static void ShipProduct(object p1, object p2, object p3) {
        Console.WriteLine("Ship!");
    }
}

然后您的代码变为:

invoices.SendEmail().ShipProduct().AdjustInventory().ToList();

当然,我不确定ToList()部分。如果你忘记打电话那么没有任何反应:)。我只是在复制你当前的扩展方法,但是不使用yield更有意义,所以你可以这样做:

invoices.SendEmail().ShipProduct().AdjustInventory();