当前位置:  开发笔记 > 编程语言 > 正文

LINQ相当于foren for IEnumerable <T>

如何解决《LINQ相当于forenforIEnumerable<T>》经验,为你挑选了11个好方法。

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

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

什么是真正的语法?



1> Fredrik Kals..:

没有ForEach扩展名IEnumerable; 仅限于List.所以你可以做到

items.ToList().ForEach(i => i.DoStuff());

或者,编写自己的ForEach扩展方法:

public static void ForEach(this IEnumerable enumeration, Action action)
{
    foreach(T item in enumeration)
    {
        action(item);
    }
}


注意ToList(),因为ToList()会创建序列的副本,这可能会导致性能和内存问题.
请注意,在第一种情况下,开发人员1)几乎不保存打字,2)不必要地分配内存以将整个序列作为列表存储在丢弃之前.在LINQ之前想一想
".Foreach()"会更好的原因很少.1.多线程,可由PLINQ完成.2.例外情况,您可能希望在循环中收集所有异常并立即抛出它; 它们是噪音代码.
PLINQ使用ForAll而不是ForEach,这就是为什么你不使用ForEach.
你应该在扩展方法中返回一个`IENumerable `.例如:`public static IEnumerable ForAll (此IEnumerable 计算,Action 动作){foreach(枚举中的T项){action(item); } return enumeration; }`
第一个例子很糟糕,但我认为第二个例子对于在适当的情况下使用时的可读性肯定是有用的.特别是在你只能传递方法的情况下,不使用lambda表示法.例如:`sequence.ForEach(doSomething);`而不是:`foreach(序列中的项目项)doSomething(item);`如果你有很多可以简化的东西,这可以产生巨大的差异.
注意 - 由于ToList执行的副本,这对于您要执行改变IEnumerable中每个项的状态的操作的情况不起作用.
更清晰和可读的方法是使用与ToList().ForEach()相对的扩展方法.虽然后者是一个快速修复,但ToList()部分没有任何关于代码功能的值.它仅仅作为适配器位于中间,这使得代码看起来更加混乱.

2> Jon Skeet..:

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

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


然而,F#有Seq.iter
正是Eric Lippert在http://blogs.msdn.com/ericlippert/archive/2009/05/18/foreach-vs-foreach.aspx中所说的内容
@Stephen Swensen:是的,F#有Seq.iter,但F#也有不可变的数据结构.当在这些上使用Seq.iter时,原始的Seq不会被修改; 返回带有副作用元素的新Seq.
@Anders - "Seq.iter"不返回任何东西,更不用说带有副作用元素的新seq了.这真的只是副作用.您可能会想到`Seq.map`,因为`Seq.iter`正好等同于`IEnumerabe <_>`上的`ForEach`扩展方法.
但是,LINQ函数通常提供的一个非常有用的东西是foreach()没有 - Linq函数采用的匿名函数也可以将索引作为参数,而对于foreach,你必须重建经典for(int i ..构造.函数式语言必须允许具有副作用的迭代 - 否则你永远无法用它们做任何工作:)我想指出典型的函数方法是返回原始枚举不变.
@Walt:我仍然不确定我是否同意,部分是因为典型的真正功能性方法不会像这样使用*foreach ......它会使用更像LINQ的函数式方法,创建一个新的序列.
作为参考,这个问题提出了同样的问题,并从"权力"得到输入:http://stackoverflow.com/questions/858978/lambda-expression-using-foreach-clause
@drstevens:说实话,我认为即便如此,我仍然可能会使用`foreach`.
我阅读了埃里克·利珀特(Eric Lippert)的文章,并阅读了有关此问答的评论,但仍然不明白为什么`IList.ForEach`会产生副作用,而`IEnumerable.ForEach`不会引起副作用-效果。我同意反对“ ForEach”的所有其他论点,但是“ ForEach”将允许使用流畅的API。有人可以解释为什么副作用对“ IEnumerable”不利,而对“ IList”不利吗?

3> drstevens..:

更新 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(this IEnumerable @this, Action 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>(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>(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()));
}



4> 小智..:

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

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


这是一个聪明的伎俩,但请不要写这样的代码.
我不得不downvote,因为虽然_technically_正确,你未来的自我和所有的继承人将永远恨你,因为而不是'foreach(项目i in GetItems()){i.DoStuff();}`你拿了更多的角色并使它变得非常扑朔迷离
好吧,但是更多可读的hack:`items.All(i => {i.DoStuff(); return true;}`
我也同意.它可能是一个聪明的小黑客,但乍一看,这个代码不清楚它在做什么,当然与标准的foreach循环相比.
我知道这是一个相当古老的帖子,但我也不得不将其投票.我同意这是一个聪明的伎俩,但它不可维护.乍一看,它似乎只对列表中的第一项做了些什么.这可能很容易导致未来更多的错误,因为编码人员误解了它的功能和它应该做的事情.
这是副作用编程和恕我直言劝阻.
更好地创建自己的扩展,因为这个"黑客"太丑了,只需要额外的CPU成本.要客观:这是一个糟糕的决定.

5> Dor Rotman..:

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

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

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

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

这是帮助看到它的测试:

[Test]
public void TestDefferedExecutionOfIEnumerableForEach()
{
    IEnumerable 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枚举.


这与使用`ToList`调用`Select`完全相同.`ForEach`的目的是不必调用`ToList`.它应该立即执行.
将执行延期的"ForEach"与立即执行的"ForEach"相比,有什么好处?延迟(这对于如何使用ForEach不方便)并没有改善这样一个事实,即如果Action有副作用,那么ForEach只执行有用的工作[通常对这样的Linq操作员提出的抱怨].那么,*这个变化有何帮助?另外,添加"ToList()"以强制它执行,没有用处.这是一场等待发生的事故."ForEach"的观点是它的副作用.如果你想要一个返回的枚举,SELECT会更有意义.

6> cdiggins..:

保持你的副作用超出我的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))

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


啊,所以Select是Map,Aggregate是Reduce.

7> regisbsb..:

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

public static class EnumerableExtensions
{
    public static IEnumerable ForEach(this IEnumerable enumeration, Action action)
    {
        foreach (var item in enumeration)
        {
            action(item);
            yield return item;
        }
    }
}



8> John Wigger..:

微软推出了针对LINQ的Interactive 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));


Interactive Extensions的工作链接:http://www.microsoft.com/download/en/details.aspx?id = 27203

9> Wolf5..:

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

IEnumerable.AsParallel().ForAll() 

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


真正.它不是线程安全的,从某个线程池中将使用不同的线程......我需要修改我的答案.我现在尝试时没有ForEach().当我思考这个问题时,我的代码必须是包含ForEach扩展的代码.

10> Tormod..:

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

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

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

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

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



11> Nenad..:

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

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

简短(st)。


现在,当我阅读我的评论时,我似乎似乎正在告诉您一些您不知道的事情。我不是那个意思 我的意思是`foreach(GetItems()中的var item)item.DoStuff();`只是一种美。
当然。这样做是为了清楚起见。这样很清楚`GetItems()`方法返回什么。
推荐阅读
喜生-Da
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有