我想在LINQ中执行以下等效操作,但我无法确定如何执行:
IEnumerable<Item> items = GetItems();
items.ForEach(i => i.DoStuff());
真正的语法是什么?
我想在LINQ中执行以下等效操作,但我无法确定如何执行:
IEnumerable<Item> items = GetItems();
items.ForEach(i => i.DoStuff());
真正的语法是什么?
当前回答
2012年7月17日更新:显然,从C#5.0开始,下面描述的foreach的行为已经改变,“在嵌套lambda表达式中使用foreach迭代变量不再产生意外结果。”这个答案不适用于C#≥5.0。
@约翰·斯基特和所有喜欢foreach关键字的人。
5.0之前的C#中的“foreach”的问题是,它与其他语言中等效的“for understanding”的工作方式不一致,也与我期望它的工作方式(此处陈述的个人观点仅是因为其他人提到了他们对可读性的看法)不一致。查看有关“修改关闭的访问”的所有问题以及“关闭被认为有害的循环变量”。这只是“有害的”,因为“foreach”是在C#中实现的。
使用@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,因为这样做并不完全牵强。显然,有更好的方法来创建这种可观察性,我只是试图证明一点。通常,订阅可观察到的代码是异步执行的,并且可能在另一个线程中执行。如果使用“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
[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()));
}
其他回答
IEnumerable没有ForEach扩展;仅适用于列表<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);
}
}
根据PLINQ(从.Net 4.0开始提供),您可以执行
IEnumerable<T>.AsParallel().ForAll()
在IEnumerable上执行并行foreach循环。
为了保持流利,可以使用以下技巧:
GetItems()
.Select(i => new Action(i.DoStuf)))
.Aggregate((a, b) => a + b)
.Invoke();
2012年7月17日更新:显然,从C#5.0开始,下面描述的foreach的行为已经改变,“在嵌套lambda表达式中使用foreach迭代变量不再产生意外结果。”这个答案不适用于C#≥5.0。
@约翰·斯基特和所有喜欢foreach关键字的人。
5.0之前的C#中的“foreach”的问题是,它与其他语言中等效的“for understanding”的工作方式不一致,也与我期望它的工作方式(此处陈述的个人观点仅是因为其他人提到了他们对可读性的看法)不一致。查看有关“修改关闭的访问”的所有问题以及“关闭被认为有害的循环变量”。这只是“有害的”,因为“foreach”是在C#中实现的。
使用@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,因为这样做并不完全牵强。显然,有更好的方法来创建这种可观察性,我只是试图证明一点。通常,订阅可观察到的代码是异步执行的,并且可能在另一个线程中执行。如果使用“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
[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()));
}
Fredrik提供了解决方案,但可能值得考虑一下为什么这不在框架中。我认为,LINQ查询运算符应该是无副作用的,符合合理的功能性世界观。很明显,ForEach恰恰相反——一个纯粹基于副作用的构造。
这并不是说这是一件坏事——只是想想这个决定背后的哲学原因。