有没有办法使用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));
答案 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扩展方法中添加副作用,例如Where
和Select
:
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();