LINQ相当于foren for IEnumerable <T>

linq foreach ienumerable

412709 观看

22回复

28707 作者的声誉

我想在LINQ中做相同的以下内容,但我无法弄清楚如何:

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

什么是真正的语法?

作者: tags2k 的来源 发布者: 2008 年 10 月 14 日

回应 (22)


782

10985 作者的声誉

决定

没有ForEach扩展名IEnumerable; 仅限于List<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 Kalseth 发布者: 14.10.2008 10:00

340

1089254 作者的声誉

Fredrik提供了修复程序,但值得考虑为什么这不是在框架中开始。我相信这个想法是LINQ查询运算符应该是无副作用的,适合用于查看世界的合理功能的方式。显然ForEach恰恰相反 - 纯粹的基于副作用的构造。

这并不是说这是一件坏事 - 只考虑决定背后的哲学原因。

作者: Jon Skeet 发布者: 14.10.2008 10:10

0

0 作者的声誉

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

考虑以下:

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

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

作者: caddzooks 发布者: 10.07.2009 02:56

19

825 作者的声誉

我采用了Fredrik的方法并修改了返回类型。

这样,该方法支持延迟执行,就像其他LINQ方法一样。

编辑:如果不清楚,此方法的任何使用必须以ToList()或任何其他方式结束,以强制该方法处理完整的可枚举。否则,将不会执行该操作!

public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
{
    foreach (T item in enumeration)
    {
        action(item);
        yield return item;
    }
}

这是帮助看到它的测试:

[Test]
public void TestDefferedExecutionOfIEnumerableForEach()
{
    IEnumerable<char> enumerable = new[] {'a', 'b', 'c'};

    var sb = new StringBuilder();

    enumerable
        .ForEach(c => sb.Append("1"))
        .ForEach(c => sb.Append("2"))
        .ToList();

    Assert.That(sb.ToString(), Is.EqualTo("121212"));
}

如果最后删除ToList(),您将看到测试失败,因为StringBuilder包含空字符串。这是因为没有方法强制ForEach枚举。

作者: Dor Rotman 发布者: 02.06.2010 08:54

-1

1 作者的声誉

又一个ForEach例子

public static IList<AddressEntry> MapToDomain(IList<AddressModel> addresses)
{
    var workingAddresses = new List<AddressEntry>();

    addresses.Select(a => a).ToList().ForEach(a => workingAddresses.Add(AddressModelMapper.MapToDomain(a)));

    return workingAddresses;
}
作者: neil martin 发布者: 17.07.2010 11:16

5

3116 作者的声誉

ForEach的目的是引起副作用。IEnumerable用于集合的延迟枚举。

当您考虑它时,这种概念差异是非常明显的。

SomeEnumerable.ForEach(item=>DataStore.Synchronize(item));

在您执行“计数”或“ToList()”或其他操作之前,这不会执行。显然不是表达的内容。

您应该使用IEnumerable扩展来设置迭代链,根据各自的源和条件定义内容。表达树是强大而有效的,但你应该学会欣赏它们的本性。而不仅仅是围绕它们进行编程以保存一些字符来覆盖惰性评估。

作者: Tormod 发布者: 31.08.2010 06:43

-2

2661 作者的声誉

如果您正在执行此操作,例如因为您需要迭代中的索引,则可以始终使用Where构造:

linqObject.Where((obj, index) => {
  DoWork(obj, index);
  return true;
}).ToArray(); //MUST CALL ToArray() or ToList() or something to execute the lazy query, or the loop won't actually execute

这具有额外的好处,即原始数组返回“未更改”(列表引用的对象是相同的,尽管它们可能没有相同的数据),这在LINQ等功能/链编程方法中通常是可取的。

作者: Walt W 发布者: 01.09.2010 05:26

33

587 作者的声誉

您可以使用FirstOrDefault()可用的扩展程序IEnumerable<T>。通过false从谓词返回,它将针对每个元素运行,但不会关心它实际上没有找到匹配。这样可以避免ToList()开销。

IEnumerable<Item> items = GetItems();
items.FirstOrDefault(i => { i.DoStuff(); return false; });
作者: Rhames 发布者: 05.10.2010 02:16

2

396 作者的声誉

现在我们可以选择......

        ParallelOptions parallelOptions = new ParallelOptions();
        parallelOptions.MaxDegreeOfParallelism = 4;
#if DEBUG
        parallelOptions.MaxDegreeOfParallelism = 1;
#endif
        Parallel.ForEach(bookIdList, parallelOptions, bookID => UpdateStockCount(bookID));

当然,这开辟了一整套新的线虫。

ps(对不起字体,这是系统决定的)

作者: Paulustrious 发布者: 02.06.2011 03:16

8

844 作者的声誉

微软推出了针对LINQInteractive Extensions实验版(也在NuGet上,有关更多链接,请参阅RxTeams的配置文件)。第9频道的视频解释得很好。

其文档仅以XML格式提供。我在Sandcastle中运行此文档,以使其具有更易读的格式。解压缩docs存档并查找index.html

在许多其他好东西中,它提供了预期的ForEach实现。它允许您编写如下代码:

int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8 };

numbers.ForEach(x => Console.WriteLine(x*x));
作者: John Wigger 发布者: 15.08.2011 03:49

35

2573 作者的声誉

更新 7/17/2012:显然从C#5.0开始,foreach下面描述的行为已经改变,“ 在嵌套的lambda表达式中使用foreach迭代变量不再产生意外结果。 ”这个答案不适用于C#≥5.0 。

@John Skeet和所有喜欢foreach关键字的人。

5.0之前的 C#中“foreach”的问题在于,它与等效的“for comprehension”在其他语言中的工作方式不一致,以及我期望它如何工作(这里所说的个人意见只是因为其他人提到了他们的关于可读性的意见)。查看有关“ 访问修改后的闭包 ”以及“ 关闭环路变量被认为有害 ”的所有问题。这只是“有害的”,因为在C#中实现了“foreach”的方式。

使用功能等效的扩展方法与@Fredrik Kalseth的答案中的示例一起使用以下示例。

public static class Enumerables
{
    public static void ForEach<T>(this IEnumerable<T> @this, Action<T> action)
    {
        foreach (T item in @this)
        {
            action(item);
        }
    }
}

为过度设计的例子道歉。我只使用Observable,因为做这样的事情并不是完全不可取的。显然有更好的方法来创建这个可观察的,我只是试图证明一个观点。通常,订阅observable的代码是异步执行的,并且可能在另一个线程中执行。如果使用“foreach”,这可能会产生非常奇怪且可能不确定的结果。

使用“ForEach”扩展方法的以下测试通过:

[Test]
public void ForEachExtensionWin()
{
    //Yes, I know there is an Observable.Range.
    var values = Enumerable.Range(0, 10);

    var observable = Observable.Create<Func<int>>(source =>
                            {
                                values.ForEach(value => 
                                    source.OnNext(() => value));

                                source.OnCompleted();
                                return () => { };
                            });

    //Simulate subscribing and evaluating Funcs
    var evaluatedObservable = observable.ToEnumerable().Select(func => func()).ToList();

    //Win
    Assert.That(evaluatedObservable, 
        Is.EquivalentTo(values.ToList()));
}

以下失败并显示错误:

预期:相当于<0,1,2,3,4,5,6,7,8,9>但是:<9,9,9,9,9,9,9,9,9,9>

[Test]
public void ForEachKeywordFail()
{
    //Yes, I know there is an Observable.Range.
    var values = Enumerable.Range(0, 10);

    var observable = Observable.Create<Func<int>>(source =>
                            {
                                foreach (var value in values)
                                {
                                    //If you have resharper, notice the warning
                                    source.OnNext(() => value);
                                }
                                source.OnCompleted();
                                return () => { };
                            });

    //Simulate subscribing and evaluating Funcs
    var evaluatedObservable = observable.ToEnumerable().Select(func => func()).ToList();

    //Fail
    Assert.That(evaluatedObservable, 
        Is.EquivalentTo(values.ToList()));
}
作者: drstevens 发布者: 01.09.2011 07:24

0

4131 作者的声誉

这种“功能方法”抽象漏掉了大量时间。语言层面没有任何东西可以防止副作用。只要您可以为容器中的每个元素调用lambda / delegate,您将获得“ForEach”行为。

这里举例说明将srcDictionary合并到destDictionary的一种方法(如果key已经存在 - 覆盖)

这是一个黑客攻击,不应该在任何生产代码中使用。

var b = srcDictionary.Select(
                             x=>
                                {
                                  destDictionary[x.Key] = x.Value;
                                  return true;
                                }
                             ).Count();
作者: Zar Shardan 发布者: 03.07.2012 12:47

4

9150 作者的声誉

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

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

简短(st)。

作者: Nenad 发布者: 08.07.2012 10:47

16

11721 作者的声誉

保持你的副作用超出我的IEnumerable

我想在LINQ中做相同的以下内容,但我无法弄清楚如何:

正如其他人国内外指出的那样,LINQ和IEnumerable方法预计不会产生副作用。

你真的想对IEnumerable中的每个项目“做点什么”吗?那foreach是最好的选择。当副作用发生在这里时,人们并不感到惊讶。

foreach (var i in items) i.DoStuff();

我打赌你不想要副作用

然而,根据我的经验,通常不需要副作用。通常情况下,有一个简单的LINQ查询等待被发现,伴随着由Jon Skeet,Eric Lippert或Marc Gravell解释如何做你想做的事情的StackOverflow.com答案!

一些例子

如果您实际上只是聚合(累积)某个值,那么您应该考虑Aggregate扩展方法。

items.Aggregate(initial, (acc, x) => ComputeAccumulatedValue(acc, x));

也许您想IEnumerable从现有值创建一个新的。

items.Select(x => Transform(x));

或者您可能想要创建一个查找表:

items.ToLookup(x, x => GetTheKey(x))

可能性的列表(不完全打算)继续下去。

作者: cdiggins 发布者: 26.12.2012 10:56

0

7097 作者的声誉

对于VB.NET,您应该使用:

listVariable.ForEach(Sub(i) i.Property = "Value")
作者: Israel Margulies 发布者: 14.03.2013 07:41

4

183923 作者的声誉

正如众多答案已经指出的那样,您可以自己轻松添加这样的扩展方法。但是,如果你不想这样做,虽然我在BCL中没有发现这样的事情,但是System如果你已经引用了Reactive Extension(如果你没有),那么命名空间中仍然有一个选项。, 你应该有):

using System.Reactive.Linq;

items.ToObservable().Subscribe(i => i.DoStuff());

尽管方法名称略有不同,但最终结果正是您所需要的。

作者: Mark Seemann 发布者: 05.01.2014 08:27

1

5559 作者的声誉

受Jon Skeet的启发,我已经通过以下方式扩展了他的解决方案:

扩展方法:

public static void Execute<TSource, TKey>(this IEnumerable<TSource> source, Action<TKey> applyBehavior, Func<TSource, TKey> keySelector)
{
    foreach (var item in source)
    {
        var target = keySelector(item);
        applyBehavior(target);
    }
}

客户:

var jobs = new List<Job>() 
    { 
        new Job { Id = "XAML Developer" }, 
        new Job { Id = "Assassin" }, 
        new Job { Id = "Narco Trafficker" }
    };

jobs.Execute(ApplyFilter, j => j.Id);

。。。

    public void ApplyFilter(string filterId)
    {
        Debug.WriteLine(filterId);
    }
作者: Scott Nimrod 发布者: 11.09.2014 12:17

1

1366 作者的声誉

ForEach也可以被链接,只需在行动后回到桩线。保持流利


Employees.ForEach(e=>e.Act_A)
         .ForEach(e=>e.Act_B)
         .ForEach(e=>e.Act_C);

Orders  //just for demo
    .ForEach(o=> o.EmailBuyer() )
    .ForEach(o=> o.ProcessBilling() )
    .ForEach(o=> o.ProcessShipping());


//conditional
Employees
    .ForEach(e=> {  if(e.Salary<1000) e.Raise(0.10);})
    .ForEach(e=> {  if(e.Age   >70  ) e.Retire();});

public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enu, Action<T> action)
{
    foreach (T item in enu) action(item);
    return enu; // make action Chainable/Fluent
}

上面的Edit2代码正在运行,但更好的版本正在使用

Taemyr指出,下面的编辑是一个错误的例子。非常感谢。

Employees.ForEach(e=>e.Salary = e.Salary * 2)
         .Where (e=> e.Salary > 10000)
         .Average(e=> e.Salary);

作者: Rm558 发布者: 23.02.2015 09:30

9

2848 作者的声誉

如果您想充当枚举卷,则应该生成每个项目。

public static class EnumerableExtensions
{
    public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
    {
        foreach (var item in enumeration)
        {
            action(item);
            yield return item;
        }
    }
}
作者: regisbsb 发布者: 31.07.2015 04:04

7

10432 作者的声誉

根据PLINQ(自.Net 4.0以来可用),你可以做到

IEnumerable<T>.AsParallel().ForAll() 

在IEnumerable上做一个并行的foreach循环。

作者: Wolf5 发布者: 24.10.2015 09:24

0

945 作者的声誉

MoreLinq有IEnumerable<T>.ForEach很多其他有用的扩展。它可能不值得依赖ForEach,但那里有很多有用的东西。

https://www.nuget.org/packages/morelinq/

https://github.com/morelinq/MoreLINQ

作者: solublefish 发布者: 19.10.2018 07:05

0

1 作者的声誉

为了保持流利,可以使用这样的技巧:

GetItems()
    .Select(i => new Action(DoStuf)))
    .Aggregate((a, b) => a + b)
    .Invoke();
作者: Alex 发布者: 28.02.2019 05:38
32x32