Enumerable<T> 内のすべての要素に対して特定のアクションを実行する
-
22-08-2019 - |
質問
を持っています 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; })
, 、しかし何も印刷されませんでした。
解決
これを取得するための簡単迅速かつ-方法があります:
Names.ToList().ForEach(e => ...);
他のヒント
あなたは現在、唯一のリストジェネリックコレクションに存在し続けるとらえどころのForEachを探しています。オンラインマイクロソフトは、LINQメソッドとして追加すべきでないかどうかについて多くの議論があります。現在、あなたがあなた自身をロールバックする必要があります:
public static void ForEach<T>(this IEnumerable<T> value, Action<T> action)
{
foreach (T item in value)
{
action(item);
}
}
All()
方法が同様の能力を提供するが、それはユースケースのすべての項目ではなく、アクションに述語テストを行うためのものです。もちろん、他のタスクを実行するように説得することができますが、これはやや(すなわち述語テストまたはアクションのAll()
のこの使用がある?)のセマンティクスを変更し、それが難しい他の人があなたのコードを解釈するために作るでしょう。
免責事項:この投稿は、私の元の回答とはもはや似ていませんが、むしろ私がそれ以来得た約 7 年間の経験を組み込んでいます。これはよく見られる質問であり、既存の回答のどれもすべての角度を実際にカバーしていないため、編集を行いました。私の元の回答を見たい場合は、 この投稿の改訂履歴.
ここで最初に理解すべきことは、次のような C# linq 操作です。 Select()
, All()
, Where()
, 、などのルーツがあります。 関数型プログラミング. 。このアイデアは、関数型プログラミングのより便利で親しみやすい部分の一部を .Net の世界に持ち込むことでした。関数型プログラミングの重要な原則は操作に副作用がないことであるため、これは重要です。これを過小評価するのは難しいです。ただし、次の場合は、 ForEach()
/each()
, 、副作用は手術のすべての目的です。追加 each()
または ForEach()
他の linq 演算子の関数型プログラミングの範囲外であるだけでなく、 彼らとは真っ向から対立している。
しかし、これでは満足できない気持ちもわかります。あなたには解決しなければならない本当の問題があります。なぜこのような象牙の塔の哲学が、本当に役立つかもしれないものの邪魔をする必要があるのでしょうか?
当時 C# 設計チームにいた Eric Lippert がここで私たちを助けてくれます。 彼は従来の方法を使用することを推奨しています 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
ループ内の操作からのキーワード。ループコマンドもリストされています 前 ループが動作するシーケンスを定義する操作に。これらの操作を最初に実行し、次にループ コマンドを実行する方がはるかに自然です。 終わり クエリ定義の。また、コードは醜いです。これを書くことができればもっと良くなりそうです:
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()
実際のところ、演算子は言語に新しい表現力を追加するものではありません。
ただし、仮説の 2 つの特徴がまだ欠けています。 ForEach()
拡張メソッド:
- 構成可能ではありません。これ以上追加することはできません
.Where()
またはGroupBy()
またはOrderBy()
後foreach
新しいステートメントを作成せずに、コードの残りの部分とインラインでループします。 - 怠惰ではありません。これらの操作は即座に実行されます。たとえば、ユーザーがコマンド ボタンを押すまで実行されない、大きな画面内の 1 つのフィールドとして操作を選択するフォームを作成することはできません。このフォームを使用すると、ユーザーはコマンドを実行する前に考えを変えることができる場合があります。これは完全に正常です (簡単 たとえ) LINQ クエリを使用しても、クエリほど単純ではありません。
foreach
.
もちろん自分で作ることもできます ForEach()
拡張メソッド。他のいくつかの回答には、このメソッドがすでに実装されています。それはそれほど複雑ではありません。しかし、それは不要な気がします。セマンティックと操作の両方の観点から、私たちがやりたいことに適合する既存のメソッドがすでに存在します。上記の不足している機能は両方とも、既存の機能を使用することで解決できます。 Select()
オペレーター。
Select()
上記の両方の例で説明した種類の変換または射影に適合します。ただし、副作用が生じることは避けたいと考えていることに留意してください。への呼び出し Select()
新しいオブジェクトまたは元のオブジェクトからの投影のいずれかを返す必要があります。これは、(必要な場合に限り) 匿名型または動的オブジェクトを使用することで支援できる場合があります。結果を元のリスト変数などに保持する必要がある場合は、いつでも呼び出すことができます。 .ToList()
それを元の変数に割り当て直します。私が一緒に作業することを好むことをここに追加します IEnumerable<T>
より具体的な型よりも可能な限り変数を使用します。
myList = myList.Select(item => new SomeType(item.value1, item.value2 *4)).ToList();
要約すれば:
- ただ固執してください
foreach
ほとんどの時間。 - いつ
foreach
本当に うまくいかない場合は(おそらくあなたが思っているほど頻繁ではないでしょう)、使用してくださいSelect()
- 使用する必要があるとき
Select()
, 、おそらく匿名型に投影することによって、一般に (プログラムから見える) 副作用を回避できます。
は、残念ながら、LINQの現在のバージョンでこれを行うには、組み込みの方法はありません。フレームワークのチームは.ForEach拡張メソッドを追加することを怠っ。これは、次のブログで今起こっている程度の良い議論があります。
http://blogs.msdn.com/kirillosenkov/アーカイブ/ 2009/1月31日/ foreach.aspxする
これは、しかし1を追加するために、むしろ簡単です。
public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action) {
foreach ( var cur in enumerable ) {
action(cur);
}
}
あなたは、LINQとしてすぐにこれを行うことはできませんIEnumerableを - あなたはArray.ForEach()を呼び出し、その後、独自の拡張メソッドを実装し、またはLINQを持つ配列へのあなたの列挙をキャストし、するか必要があります。
Array.ForEach(MyCollection.ToArray(), x => x.YourMethod());
コレクションは値型であり、あなたは、コレクションの要素をこのように変更した場合、それは元のコレクションの要素には影響を与えません、なぜなら値の型と構造体の働き方のことを注意してください。
LINQクエリの特徴ではなく、それはあなたが(潜在的な副作用を持つ)メソッドを実行できるようになるので、あなたが
foreachの(名前の文字列名)
Console.WriteLineを(名);
さて、あなたはまた、単にonelinerにそれをフォーマットし、標準foreach
キーワードを使用することができます:
foreach(var n in Names.Where(blahblah)) DoStuff(n);
申し訳ありませんが、このオプションがここに値すると思った:)
リストのオフをForEachメソッドがあります。あなたは.ToList()メソッドを呼び出すことによって、リストに列挙を変換し、そのオフの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;
}
}
パラレルのLINQを使用します:
Names.AsParallel()。ForAllを(名前=> ...)