2014-03-05 61 views
1

是否有使用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)); 
+4

或許不會,因爲'行動(ELEM);副作用'惡臭。 –

+0

確實;如果有的話,它將是一個'Func ()'而不是'Action';然後你可以用'Select()'來使用它。 –

+2

[LINQ設計者主動選擇不包含這種方法。](http://blogs.msdn.com/b/ericlippert/archive/2009/05/18/foreach-vs-foreach.aspx)這完全相反「LINQ」的整個設計。只需使用'foreach'循環。 – Servy

回答

2

微軟發佈了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)); 

將工作開箱。

0

你的方法是等效的只是使用選擇:

var x = list.Select(x => { x.Something(); return x; }); 
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; 
} 

恕我直言,它可能混淆依靠你的枚舉的用戶,以確保您的行動調用一次,所以我一般會避免這樣的設計。

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(); 
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()的一部分。如果你忘記打電話,那麼沒有任何反應:)。我只是複製當前的擴展方法,但它可能會更有意義,不使用屈服,讓你可以只是做:

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

我喜歡這個......它的使用語法簡潔明瞭。但是,它並不允許在枚舉中的每個元素上執行具有副作用的任意通用操作...您必須爲每個要執行的新操作創建一個擴展方法(您還沒有有一個擴展...) –

+0

絕對。正如我所說的,這是如果你真的想擁抱方法鏈接,而不是做某種事情。我希望你會有其他類正在做繁重的工作,所以擴展方法只是調用。所以,添加一個新的擴展方法並不是什麼大不了的事,大概你不會每天都這樣做。你會比你要寫的方法更頻繁地調用方法:)。而且,正如你所說,它更簡潔。我寧願寫這樣的代碼,然後是所有'.Do()'。我的兩分錢。 – aquinas