IEnumerable <T>のforeachに相当するLINQ


717

LINQで以下と同等のことを実行したいのですが、方法がわかりません。

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

実際の構文は何ですか?


99
考えてみましょう「foreachの」対「のForEach」エリックリペットによると。これは、使用/供給しないことの正当な理由を提供しますForEach()

4
@pst:私は自分のプロジェクトにこの拡張メソッドを盲目的に貼り付けていました。その記事をありがとう。
Robin Maben

2
拡張子を持つMoreLINQがあります。ForEach
Uwe Keim 2014

2
それほどセクシーではありませんが、これはToList経由でコピーを作成せずに1行に収まりますforeach (var i in items) i.Dostuff();
James Westgate

1
これらのリンクのいくつかは死んでいます!そんなに投票された記事は何でしたか!
ウォーレン

回答:


863

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

213
ToList()はシーケンスのコピーを作成するため、ToList()には注意してください。これにより、パフォーマンスとメモリの問題が発生する可能性があります。
decasteljau 2009

15
".Foreach()"の方が優れている理由はいくつかあります。1. PLINQによって可能なマルチスレッド。2.例外。ループ内のすべての例外を収集して、一度にスローすることができます。そしてそれらはノイズコードです。
Dennis C

12
PLINQはForEachではなくForAllを使用するため、ForEachは使用しません。
user7116 2010

9
IENumerable<T>拡張メソッドでを返す必要があります。例:public static IEnumerable<T> ForAll<T>(this IEnumerable<T> numeration, Action<T> action) { foreach (T item in enumeration) { action(item); } return enumeration; }
Kees C. Bakker

22
最初のケースでは、開発者は1)タイピングをほとんど保存せず、2)不要なメモリを割り当ててシーケンス全体をリストとして保存してから破棄します。LINQの前に考える
Mark Sowul 2013

364

Fredrikが修正を提供しましたが、これが最初からフレームワークにない理由を検討する価値があるかもしれません。この考えは、LINQクエリ演算子は副作用がなく、世界を見るのに適度に機能する方法に適合しているべきだと考えています。明らかにForEachは正反対であり、純粋に副作用ベースの構造です。

これが悪いことだと言っているのではなく、決定の背後にある哲学的理由について考えているだけです。


44
それでも、F#にはSeq.iterがあります
Stephen

6
ただし、LINQ関数が通常提供する非常に便利な点が1つあり、foreach()では提供されません。Linq関数によって取得される匿名関数はインデックスをパラメーターとして使用することもできますが、foreachを使用すると、(int i .. )構築します。そして、関数型言語は副作用のある反復を許可する必要があります-そうでなければ、それらを使って1つの作業を行うことは決してできません:)私は、典型的な関数型メソッドは元の列挙型を変更せずに返すことになると指摘したいと思います。
Walt W

3
@ウォルト:典型的な真に機能的なアプローチではこのようなforeachが使用されないこともあるため、同意するかどうかまだわかりません... LINQのような機能的なアプローチを使用して、新しいシーケンスを作成します。
Jon Skeet

12
@Stephen Swensen:はい、F#にはSeq.iterがありますが、F#にも不変のデータ構造があります。これらでSeq.iterを使用する場合、元のSeqは変更されません。副作用のある要素を持つ新しいSeqが返されます。
Anders

7
@Anders- Seq.iter何も返されません。副作用の要素を持つ新しいシーケンスは言うまでもありません。それは本当に副作用についてです。は、の拡張メソッドとまったく同じSeq.mapであるため、考えているかもしれません。Seq.iterForEachIEnumerabe<_>
Joel Mueller

39

アップデート 7/17/2012:どうやらC#5.0以降では、foreach以下の動作が変更され、「ネストされたラムダ式で反復変数を使用してもforeach予期しない結果が発生しなくなりました。」この回答はC#≥5.0には適用されません。

@John Skeetとforeachキーワードを好むすべての人。

5.0より前の C#の「foreach」の問題は、同等の「for comprehension」が他の言語でどのように機能するか、および私がそれがどのように機能するかを予想できないことです(他の人が言及しているため、ここで述べた個人的な意見)読みやすさに関する意見)。「変更されたクロージャへのアクセス」および「有害と見なされるループ変数のクローズ」に関するすべての質問を参照してください。"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のみを使用しています。これは、このようなことをするために完全にフェッチされているわけではないためです。明らかに、このオブザーバブルを作成するためのより良い方法があり、私はポイントを示すことだけを試みています。通常、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、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()));
}

リシャーパー警告は何と言っていますか?
reggaeguitar

1
@reggaeguitar C#5.0がリリースされる前から、C#を7年または8年使用していなかったため、ここで説明する動作が変更されました。当時、この警告が表示されました。stackoverflow.com/ questions / 1688465 /…。私はそれがまだこれについて警告しているとは思えません。
drstevens

34

FirstOrDefault()で利用可能な拡張機能を使用できますIEnumerable<T>false述語から戻ることにより、要素ごとに実行されますが、実際に一致が見つからないことは問題になりません。これにより、ToList()オーバーヘッドが回避されます。

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

11
私も同意します。巧妙な小さなハックかもしれませんが、一見したところ、このコードは標準のforeachループと比較して、何をしているのか明確ではありません。
Connell 2012

26
私がいる間のでdownvoteしていた技術的に代わりのために正しい、あなたの将来の自己とすべてのあなたの後継者は永遠にあなたを嫌いになるforeach(Item i in GetItems()) { i.DoStuff();}あなたはより多くの文字を取って、それは非常に混乱製
マーク・Sowul

14
OK、しかし多分もっと読みやすいハック:items.All(i => { i.DoStuff(); return true; }
nawfal '19

5
私はこれがかなり古い投稿であることを知っていますが、それも反対票を投じなければなりませんでした。私はそれが巧妙なトリックであることに同意しますが、それは維持できません。一見、リストの最初の項目のみで何かをしているように見えます。これは、プログラマーがどのように機能し、何をしているのかを誤解している将来のバグを簡単に引き起こす可能性があります。
Lee

4
これは副作用プログラミングであり、私見は推奨されません。
アニッシュ

21

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に列挙を強制するメソッドがないためです。


の代替実装ForEachは興味深いですが、の動作と一致しません。List.ForEachその署名はですpublic void ForEach(Action<T> action)。また、署名がであるObservable.ForEachへの拡張機能の動作と一致しません。Scalaコレクションの同等のものであるFWIWは、遅延のあるものであっても、C#のvoidと同等の戻り型を持っています。IObservable<T>public static void ForEach<TSource>(this IObservable<TSource> source, Action<TSource> onNext)ForEach
drstevens 2011年

これが最良の答えです。遅延実行+ Fluent API。
アレクサンダーダニロフ2015

3
これは、での呼び出しSelectとまったく同じように機能しToListます。の目的はForEach呼び出す必要がないことToListです。すぐに実行されます。
t3chb0t 2016

3
この "ForEach"を使用すると、すぐに実行されるのに対して、実行が延期される利点は何でしょうか?延期(ForEachの使用方法に不便)は、アクションに副作用がある場合(そのようなLinq演算子に対して行われる通常の不満)にのみForEachが有用な作業を実行するという事実を改善しません。だから、どのように、この変更のヘルプは?さらに、実行を強制するために追加された「ToList()」は、有用な目的を果たしません。起こるのを待っているその事故。「ForEach」のポイントは、その副作用です。返された列挙が必要な場合は、SELECTの方が理にかなっています。
ToolmakerSteve 2017

20

私のIEnumerableにあなたの副作用を入れないでください

LINQで以下と同等のことを実行したいのですが、方法がわかりません。

他の人がここや海外で指摘したように、LINQとIEnumerableメソッドには副作用がないことが期待されます。

IEnumerableの各項目に本当に「何か」を実行しますか?次にforeach、最良の選択です。ここで副作用が発生しても、人々は驚かないでしょう。

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

私はあなたが副作用を望んでいないに違いない

しかし、私の経験では、副作用は通常必要ありません。たいていの場合、Jon Skeet、Eric Lippert、またはMarc GravellのいずれかによるStackOverflow.comの回答を伴う、発見されるのを待つ単純なLINQクエリがあります。

いくつかの例

実際にいくつかの値を単に集計(累積)する場合は、Aggregate拡張メソッドを検討する必要があります。

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

おそらくIEnumerable、既存の値から新しいものを作成する必要があります。

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

または、ルックアップテーブルを作成することもできます。

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

可能性のリスト(完全に意図されたものではありませんが)は何度も続きます。


7
ああ、そのため、選択はマップであり、集計は削減です。
Darrel Lee

17

列挙ロールとして機能する場合は各アイテムを生成する必要があります。

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

これは実際にアイテムを返すときのForEachではなく、タップのようなものです。
EluciusFTW

10

MicrosoftによるLINQへInteractive Extensionsの試験的なリリースがあります(NuGetでも、リンクについてはRxTeamsのプロファイルを参照してください)。チャンネル9のビデオは、それをよく説明しています。

そのドキュメントはXML形式でのみ提供されます。このドキュメントをサンドキャッスルで実行して、より読みやすい形式にすることができます。docsアーカ​​イブを解凍し、index.htmlを探します。

他の多くの利点の中で、それは期待されるForEach実装を提供します。次のようなコードを記述できます。

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

numbers.ForEach(x => Console.WriteLine(x*x));

2
Interactive Extensionsへのリンク:microsoft.com/download/en/details.aspx
id=27203

8

PLINQ(.Net 4.0以降で利用可能)によると、

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

IEnumerableで並列foreachループを実行します。


アクションがスレッドセーフである限り、すべてが良いです。しかし、スレッドセーフでないアクションを実行するとどうなりますか?それはかなりの騒乱を引き起こす可能性があります...
shirbr510

2
本当です。スレッドセーフではありません。いくつかのスレッドプールから異なるスレッドが使用されます...そして、答えを修正する必要があります。今試してみると、ForEach()はありません。これについて考えたとき、ForEachを使用したExtensionを含むコードであったに違いありません。
Wolf5 2016年

6

ForEachの目的は、副作用を引き起こすことです。IEnumerableは、セットの遅延列挙用です。

この概念的な違いは、考えてみると非常に明白です。

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

これは、「count」、「ToList()」、または何かを実行するまで実行されません。明らかに表現されているものではありません。

反復のチェーンをセットアップし、それぞれのソースと条件によってコンテンツを定義するには、IEnumerable拡張機能を使用する必要があります。式ツリーは強力で効率的ですが、その性質を理解することを学ぶ必要があります。そして、遅延評価を上書きするいくつかの文字を保存するために、それらの周りのプログラミングだけではありません。


5

多くの人がそれについて言及しましたが、私はそれを書き留めなければなりませんでした。これは最も明確/最も読みやすいですか?

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

短くてシンプル(st)。


最初の行全体をドロップして、GetItemsをforeachで直接使用できます
tymtam

3
もちろん。わかりやすくするために、このように書かれています。これにより、どのGetItems()メソッドが返されるかが明確になります。
Nenad

4
私のコメントを読むと、あなたが知らないことを言っているように見えます。そういう意味ではありませんでした。つまり、それforeach (var item in GetItems()) item.DoStuff();は単なる美しさです。
ティムタム

4

今、私たちはオプションがあります...

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

もちろん、これはまったく新しいスレッドワームの缶を開きます。

ps(フォントについて申し訳ありません。システムが決定したものです)


4

多くの回答がすでに指摘しているように、そのような拡張メソッドを自分で簡単に追加できます。ただし、これを実行したくない場合は、BCLでこのようなことは何も認識していませんSystemが、既にReactive Extensionへの参照がある場合(および、あなたが持っているべきです):

using System.Reactive.Linq;

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

メソッド名は少し異なりますが、最終結果はまさにあなたが探しているものです。


そのパッケージは現在廃止されているようです
reggaeguitar

3

ForEachはChainedにすることもでき、アクションの後にパイルラインに戻すだけです。流暢さを保つ


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
}

編集:レイジーバージョンは次のように、歩留まりのリターンを使用して、この

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

レイジーバージョンは具体化する必要があります(ToList()など)。それ以外の場合、何も起こりません。ToolmakerSteveからの以下のすばらしいコメントを参照してください。

IQueryable<Product> query = Products.Where(...);
query.ForEachLazy(t => t.Price = t.Price + 1.00)
    .ToList(); //without this line, below SubmitChanges() does nothing.
SubmitChanges();

ForEach()とForEachLazy()の両方をライブラリに保持しています。


2
これをテストしましたか?このメソッドが直感的に動作する状況はいくつかあります。より良いでしょうforeach(T item in enu) {action(item); yield return item;}
Taemyr

2
これにより、複数の列挙が発生します
regisbsb

面白い。上記ではなく、3つのアクションのシーケンスを実行したい場合は、foreach (T item in enu) { action1(item); action2(item); action3(item); }?単一の明示的なループ。action2を開始する前に、すべてのaction1 を実行することが重要だったと思います。
ToolmakerSteve 2017

@ Rm558-「利回り」を使用したアプローチ。プロ:元のシーケンスを列挙するのは1回だけなので、より広い範囲の列挙で正しく機能します。欠点:最後に ".ToArray()"を忘れても、何も起こりません。失敗は明白ですが、長い間見過ごされないため、大きなコストではありません。私にとって「きれいに感じる」わけではありませんが、この道(ForEachオペレーター)を進んでいる場合は、適切なトレードオフです。
ToolmakerSteve 2017

1
このコードは、トランザクションの観点から安全ではありません。失敗した場合はどうなりますProcessShipping()か?あなたは買い手にメールを送り、彼のクレジットカードに請求しますが、彼のものを決して送らないのですか?確かに問題を求めています。
zmechanic 2017

2

この「機能的アプローチ」の抽象化は、大きな時間を浪費します。言語レベルでは何も副作用を防ぎません。コンテナ内のすべての要素に対してラムダ/デリゲートを呼び出すことができる限り、「ForEach」動作が得られます。

たとえば、srcDictionaryをdestDictionaryにマージする1つの方法(キーがすでに存在する場合-上書き)

これはハックであり、実稼働コードでは使用しないでください。

var b = srcDictionary.Select(
                             x=>
                                {
                                  destDictionary[x.Key] = x.Value;
                                  return true;
                                }
                             ).Count();

6
この免責事項を追加する必要がある場合は、投稿しないでください。ただ私の意見です。
Andrew Grothe

2
地獄はこれより優れていforeach(var tuple in srcDictionary) { destDictionary[tuple.Key] = tuple.Value; }ますか?量産コードでない場合、どこで、なぜこれを使用する必要がありますか?
Mark Sowul 2013

3
これは、原則として可能であることを示しています。それはまた、OPが要求したことを偶然に行い、私はそれが本番環境で使用されるべきではなく、まだ好奇心が強く、教育的価値があることを明確に示しました。
Zar Shardan 2013

2

ジョン・スキートに触発されて、私は彼の解決策を次のように拡張しました:

拡張方法:

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


1

リンク拡張メソッドには副作用がないという考えには敬意を払いません(そうでない場合だけでなく、どのデリゲートも副作用を実行できます)。

以下を検討してください。

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

この例が実際に示しているのは、アクションを定義する値をデコードして変換する大きなスイッチコンストラクトを記述しなくても、要素のシーケンスに副作用がある多くの可能なアクションの1つを呼び出すことができる一種の遅延バインディングです。対応するメソッドに入れます。


9
拡張メソッドに副作用があるかどうかと、あるべきかどうかには違いがあります。あなたはそれが持つことができることを指摘しているだけですが、それらが副作用を持つべきではないと提案する非常に正当な理由があるかもしれません。関数型プログラミングでは、すべての関数に副作用はありません。関数型プログラミング構造を使用する場合は、関数がそうであると想定する必要があります。
Dave Van den Eynde、2010

1

流暢さを保つには、そのようなトリックを使うことができます:

GetItems()
    .Select(i => new Action(i.DoStuf)))
    .Aggregate((a, b) => a + b)
    .Invoke();


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

4
なんという記憶の無駄。これはToListを呼び出してリストに追加します。なぜこれは単にアドレスを返さないのですか?Select(a => MapToDomain(a))?
Mark Sowul 2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.