协慌网

登录 贡献 社区

LINQ 相当于 IEnumerable <T> 的 foreach

我想在 LINQ 中执行以下操作,但是我不知道如何操作:

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

真正的语法是什么?

答案

IEnumerable ForEach 扩展; 仅用于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 提供了修复程序,但是可能值得考虑为什么它不在框架之内。我相信 LINQ 查询运算符应该是无副作用的,并以合理的功能来看待世界。显然,ForEach 正好相反 -纯粹基于副作用的构造。

这并不是说这是一件坏事,只是想想这个决定背后的哲学原因。

2012 年 7 月 17 日更新:显然,从 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,因为要做这样的事情并不遥不可及。显然,有更好的方法来创建此可观察的对象,我只是在尝试说明一个观点。通常,预订可观察对象的代码是异步执行的,并且有可能在另一个线程中执行。如果使用 “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>

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