我想在LINQ中做相同的以下内容,但我无法弄清楚如何:
IEnumerable- items = GetItems(); items.ForEach(i => i.DoStuff());
什么是真正的语法?
没有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); } }
Fredrik提供了修复程序,但值得考虑为什么这不是在框架中开始.我相信这个想法是LINQ查询运算符应该是无副作用的,适合用于查看世界的合理功能的方式.显然ForEach恰恰相反 - 纯粹的基于副作用的构造.
这并不是说这是一件坏事 - 只考虑决定背后的哲学原因.
更新 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())); }
您可以使用FirstOrDefault()
可用的扩展程序IEnumerable
.通过false
从谓词返回,它将针对每个元素运行,但不会关心它实际上没有找到匹配.这样可以避免ToList()
开销.
IEnumerable- items = GetItems(); items.FirstOrDefault(i => { i.DoStuff(); return false; });
我采用了Fredrik的方法并修改了返回类型.
这样,该方法支持延迟执行,就像其他LINQ方法一样.
编辑:如果不清楚,此方法的任何使用必须以ToList()或任何其他方式结束,以强制该方法处理完整的可枚举.否则,将不会执行该操作!
public static IEnumerableForEach (this IEnumerable enumeration, Action action) { foreach (T item in enumeration) { action(item); yield return item; } }
这是帮助看到它的测试:
[Test] public void TestDefferedExecutionOfIEnumerableForEach() { IEnumerableenumerable = 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枚举.
我想在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))
可能性的列表(不完全打算)继续下去.
如果您想充当枚举卷,则应该生成每个项目.
public static class EnumerableExtensions { public static IEnumerableForEach (this IEnumerable enumeration, Action action) { foreach (var item in enumeration) { action(item); yield return item; } } }
微软推出了针对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));
根据PLINQ(自.Net 4.0以来可用),你可以做到
IEnumerable.AsParallel().ForAll()
在IEnumerable上做一个并行的foreach循环.
ForEach的目的是引起副作用.IEnumerable用于集合的延迟枚举.
当您考虑它时,这种概念差异是非常明显的.
SomeEnumerable.ForEach(item=>DataStore.Synchronize(item));
在您执行"计数"或"ToList()"或其他操作之前,这不会执行.显然不是表达的内容.
您应该使用IEnumerable扩展来设置迭代链,根据各自的源和条件定义内容.表达树是强大而有效的,但你应该学会欣赏它们的本性.而不仅仅是围绕它们进行编程以保存一些字符来覆盖惰性评估.
很多人提到它,但是我不得不写下来。这不是最清晰/最易读的吗?
IEnumerable- items = GetItems(); foreach (var item in items) item.DoStuff();
简短(st)。