web-dev-qa-db-ja.com

Enumerable <T>のすべての要素に対して特定のアクションを実行する

私は_Enumerable<T>_を持っていて、Selectのような要素ごとにアクションを実行できるようにするメソッドを探していますが、副作用があります。何かのようなもの:

_string[] Names = ...;
Names.each(s => Console.Writeline(s));
_

または

_Names.each(s => GenHTMLOutput(s));   
// (where GenHTMLOutput cannot for some reason receive the enumerable itself as a parameter)
_

Select(s=> { Console.WriteLine(s); return s; })を試しましたが、何も出力されませんでした。

45
Daniel Magliola

これを取得する簡単な方法は次のとおりです。

Names.ToList().ForEach(e => ...);
55
Jay Bazuzi

現在、Listジェネリックコレクションにのみ存在する、とらえどころのないForEachを探しています。 MicrosoftがこれをLINQメソッドとして追加する必要があるかどうかについては、オンラインで多くの議論があります。現在、自分でロールする必要があります。

_public static void ForEach<T>(this IEnumerable<T> value, Action<T> action)
{
  foreach (T item in value)
  {
    action(item);
  }
}
_

All()メソッドは同様の機能を提供しますが、これはユースケースであり、アクションではなくすべてのアイテムに対して述語テストを実行します。もちろん、他のタスクを実行するように説得することもできますが、これによりセマンティクスが多少変更され、他の人がコードを解釈するのが難しくなります(つまり、述語テストまたはアクションでのAll()の使用ですか?) 。

18
Jeff Yates

免責事項:この投稿は、私の元の回答に似ていませんが、それ以来、私が7年以上にわたって得てきた経験を取り入れています。これは非常によく見られる質問であり、既存の回答のどれも実際にすべての角度をカバーしていないため、編集を行いました。私の元の回答を確認したい場合は、 この投稿の変更履歴 で確認できます。


ここで最初に理解する必要があるのは、Select()All()Where()などのC#linq操作であり、そのルーツは function programming です。 。アイデアは、関数型プログラミングのより便利で親しみやすい部分を.Netの世界にもたらすことでした。関数型プログラミングの主要な目的は、操作に副作用がないようにすることであるため、これは重要です。これを過小評価するのは難しい。ただし、ForEach()/each()の場合、副作用は操作の全体です。 each()またはForEach()を追加することは、他のlinq演算子の関数型プログラミングの範囲外であるだけでなく、それらに直接反対します。

しかし、私はこれが満足できないと感じていることを理解しています。あなたが解決する必要がある本当の問題があります。なぜこの象牙の塔の哲学は、本当に役立つかもしれないものの邪魔になるのでしょうか?

当時C#の設計チームにいたエリックリッパーは、ここで私たちを助けることができます。 従来のforeachループを使用することをお勧めします:

[ForEach()]は、言語に新しい表現力を追加しません。これを行うと、この完全に明確なコードを書き換えることができます。

foreach(Foo foo in foos){ statement involving foo; }

このコードに:

foos.ForEach(foo=>{ statement involving foo; });

彼のポイントは、構文オプションを注意深く見た場合、ForEach()拡張と従来のforeachループから新しいものは何も得られないということです。部分的に同意しません。あなたがこれを持っていると想像してください:

_ foreach(var item in Some.Long(and => possibly)
                         .Complicated(set => ofLINQ)
                         .Expression(to => evaluate)
 {
     // now do something
 } 
_

このコードは、foreachキーワードをループ内の操作から分離するため、意味がわかりにくくなっています。また、ループが動作するシーケンスを定義する操作にループコマンドpriorをリストします。これらの操作を最初に行い、次にループ定義コマンドをクエリ定義のendに置くことは、はるかに自然なことです。また、コードは醜いです。これを書けるようにするともっといいようです:

_Some.Long(and => possibly)
   .Complicated(set => ofLINQ)
   .Expression(to => evaluate)
   .ForEach(item => 
{
    // now do something
});
_

しかし、ここでもやがてエリックの視点にたどり着きました。上記のようなコードが追加の変数を呼び出していることに気づきました。そのようなLINQ式の複雑なセットがある場合は、最初にLINQ式の結果を新しい変数に割り当てることにより、貴重な情報をコードに追加できます。

_var queryForSomeThing = Some.Long(and => possibly)
                        .Complicated(set => ofLINQ)
                        .Expressions(to => evaluate);
foreach(var item in queryForSomeThing)
{
    // now do something
}
_

このコードはより自然に感じられます。 foreachキーワードを残りのループの横、クエリ定義の後に戻します。何よりも、変数名は、LINQクエリの目的を理解しようとする将来のプログラマーに役立つ新しい情報を追加できます。この場合も、目的のForEach()演算子は、言語に新しい表現力を追加していません。

ただし、架空のForEach()拡張メソッドの2つの機能がまだありません。

  1. 構成できません。 foreachループの後に、残りのコードをインラインで追加して.Where()またはGroupBy()またはOrderBy()を追加することはできません。新しいステートメント。
  2. 怠惰ではありません。これらの操作はすぐに行われます。たとえば、ユーザーがコマンドボタンを押すまで操作されない大きな画面で、ユーザーが操作を1つのフィールドとして選択するフォームを作成することはできません。このフォームにより、ユーザーはコマンドを実行する前に考えを変えることができます。これは、LINQクエリでは完全に正常(簡単でも)ですが、foreachでは単純ではありません。

もちろん、独自のForEach()拡張メソッドを作成することもできます。他のいくつかの回答には、このメソッドの実装がすでにあります。それほど複雑ではありません。しかし、それは不必要だと思います。セマンティックと運用の両方の観点から実行したいことに適合する既存のメソッドがすでに存在します。上記の不足している機能はどちらも、既存のSelect()演算子を使用して対処できます。

Select()は、上記の両方の例で説明されている種類の変換または投影に適合します。ただし、副作用の発生は避けたいことを覚えておいてください。 Select()を呼び出すと、元のオブジェクトから新しいオブジェクトまたは投影が返されます。これは、匿名型または動的オブジェクト(必要な場合のみ)を使用することで支援できる場合があります。たとえば、元のリスト変数に結果を保持する必要がある場合は、いつでも.ToList()を呼び出して、元の変数に割り当てることができます。ここでは、具体的な型よりも_IEnumerable<T>_変数をできるだけ使用することをお勧めします。

_myList = myList.Select(item => new SomeType(item.value1, item.value2 *4)).ToList();
_

要約すれば:

  1. ほとんどの場合、foreachに固執してください。
  2. foreachreallyが機能しない場合(おそらくそれほど頻繁ではありません)、Select()を使用します
  3. Select()を使用する必要がある場合でも、おそらく匿名型に投影することで、(プログラムから見える)副作用を通常は回避できます。
10
Joel Coehoorn

残念ながら、現在のバージョンのLINQでは、これを行う組み込みの方法はありません。フレームワークチームは、.ForEach拡張メソッドの追加を怠っていました。これについては、次のブログで今すぐ良い議論があります。

http://blogs.msdn.com/kirillosenkov/archive/2009/01/31/foreach.aspx

ただし、追加するのは簡単です。

public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action) {
  foreach ( var cur in enumerable ) {
    action(cur);
  }
}
6
JaredPar

LINQとIEnumerableを使用してこれをすぐに実行することはできません。独自の拡張メソッドを実装するか、列挙型をLINQを使用して配列にキャストしてからArray.ForEach()を呼び出す必要があります。

Array.ForEach(MyCollection.ToArray(), x => x.YourMethod());

値タイプと構造体の動作方法により、コレクションが値タイプであり、この方法でコレクションの要素を変更した場合、元のコレクションの要素には影響がないことに注意してください。

4
Tamas Czinege

LINQは更新機能ではなくクエリ機能として設計されているため、IEnumerable <T>でメソッドを実行する拡張機能はありません。これにより、メソッドを実行できる可能性があります(副作用がある可能性があります)。この場合、あなたはただ固執することもできます

foreach(名前の文字列名)
Console.WriteLine(name);

2
Peter Morris

Parallel Linqの使用:

Names.AsParallel()。ForAll(name => ...)

1
Chris Xue

まあ、標準のforeachキーワードを使用して、ワンライナーにフォーマットすることもできます。

foreach(var n in Names.Where(blahblah)) DoStuff(n);

申し訳ありませんが、このオプションはここにあるに値すると思いました:)

1
Serge Shultz

ListにはForEachメソッドがあります。 .ToList()メソッドを呼び出してEnumerableをListに変換し、それからForEachメソッドを呼び出すことができます。

あるいは、IEnumerableから独自のForEachメソッドを定義していると聞いたことがあります。これは、基本的にForEachメソッドを呼び出すことで実現できますが、代わりに拡張メソッドでラップします。

public static class IEnumerableExtensions
{
    public static IEnumerable<T> ForEach<T>(this IEnumerable<T> _this, Action<T> del)
    {
        List<T> list = _this.ToList();
        list.ForEach(del);
        return list;
    }
}
0
David Morton