Zip
欠落している関数について尋ねる別の質問に触発されました:
インターフェイスForEach
に拡張メソッドがないのはなぜですか? IEnumerable
それともどこか?ForEach
メソッドを取得する唯一のクラスはList<>
. 欠落している理由はありますか、おそらくパフォーマンスですか?
Zip
欠落している関数について尋ねる別の質問に触発されました:
インターフェイスForEach
に拡張メソッドがないのはなぜですか? IEnumerable
それともどこか?ForEach
メソッドを取得する唯一のクラスはList<>
. 欠落している理由はありますか、おそらくパフォーマンスですか?
ほとんどの場合、その言語には既にforeach
ステートメントが含まれています。
次のようなものは見たくありません。
list.ForEach( item =>
{
item.DoSomething();
} );
それ以外の:
foreach(Item item in list)
{
item.DoSomething();
}
後者は、ほとんどの状況でより明確で読みやすいですが、入力するのに少し時間がかかるかもしれません.
しかし、私はその問題に対するスタンスを変えたことを認めなければなりません。ForEach()
拡張メソッドは、状況によっては実際に役立ちます。
ステートメントとメソッドの主な違いは次のとおりです。
ForEach()
コンパイル時に行われます (ビッグプラス!)これらはすべて、ここで多くの人が指摘した素晴らしい点であり、人々が機能を見逃している理由がわかります. Microsoft が次のフレームワーク イテレーションで標準の ForEach メソッドを追加してもかまいません。
ForEach メソッドは LINQ の前に追加されました。ForEach 拡張機能を追加すると、拡張メソッドの制約により、List インスタンスに対して呼び出されることはありません。追加されなかったのは、既存のものに干渉しないためだと思います。
ただし、このちょっとした便利な機能が本当に恋しい場合は、独自のバージョンを展開できます
public static void ForEach<T>(
this IEnumerable<T> source,
Action<T> action)
{
foreach (T element in source)
action(element);
}
この拡張メソッドを書くことができます:
// Possibly call this "Do"
IEnumerable<T> Apply<T> (this IEnumerable<T> source, Action<T> action)
{
foreach (var e in source)
{
action(e);
yield return e;
}
}
長所
チェーンを許可します:
MySequence
.Apply(...)
.Apply(...)
.Apply(...);
短所
反復を強制するために何かをするまで、実際には何もしません。そのため、 と呼ぶべきではありません.ForEach()
。最後に書く.ToList()
か、この拡張メソッドも書くことができます:
// possibly call this "Realize"
IEnumerable<T> Done<T> (this IEnumerable<T> source)
{
foreach (var e in source)
{
// do nothing
;
}
return source;
}
これは、出荷されている C# ライブラリから大きく逸脱している可能性があります。拡張メソッドに慣れていない読者は、コードをどうすればよいかわかりません。
ここでの議論は答えを与えます:
実際、私が目撃した特定の議論は、実際には機能的純度にかかっていました。式では、副作用がないという仮定が頻繁に行われます。ForEach を使用すると、単に我慢するのではなく、具体的に副作用を招きます。-- キース・ファーマー (パートナー)
基本的に、拡張メソッドを機能的に「純粋」に保つという決定が下されました。Enumerable 拡張メソッドを使用する場合、ForEach は副作用を助長しますが、これは意図したものではありませんでした。
ほとんどの場合、組み込みのコンストラクトを使用する方が良いことに同意しますが、 ForEach<> 拡張機能でこのバリエーションを使用すると、自分foreach
で通常のインデックスを管理するよりも少し便利だと思います。foreach
public static int ForEach<T>(this IEnumerable<T> list, Action<int, T> action)
{
if (action == null) throw new ArgumentNullException("action");
var index = 0;
foreach (var elem in list)
action(index++, elem);
return index;
}
例
var people = new[] { "Moe", "Curly", "Larry" };
people.ForEach((i, p) => Console.WriteLine("Person #{0} is {1}", i, p));
あなたに与えるでしょう:
Person #0 is Moe
Person #1 is Curly
Person #2 is Larry
1 つの回避策は、次のように書くこと.ToList().ForEach(x => ...)
です。
長所
理解しやすい - 読者は、追加の拡張メソッドではなく、C# に同梱されているものを知るだけで済みます。
構文上のノイズは非常に軽度です (少し余分なコードを追加するだけです)。
.ForEach()
とにかく、ネイティブはコレクション全体を実現する必要があるため、通常は余分なメモリは必要ありません。
短所
操作の順序は理想的ではありません。1つの要素に気づき、それを実行し、繰り返したいと思います。このコードは、最初にすべての要素を実現し、次にそれらを順番に処理します。
リストが例外をスローすることに気付いた場合、単一の要素を操作することはできません。
列挙が (自然数のように) 無限である場合は、運が悪いです。
私はいつも自分自身が疑問に思っていたので、いつもこれを持ち歩いています。
public static void ForEach<T>(this IEnumerable<T> col, Action<T> action)
{
if (action == null)
{
throw new ArgumentNullException("action");
}
foreach (var item in col)
{
action(item);
}
}
ちょっとした拡張メソッド。
そのため、ForEach 拡張メソッドは LINQ 拡張メソッドのように値を返さないため適切ではないという事実について多くのコメントがありました。これは事実に基づく声明ですが、完全に真実ではありません。
LINQ 拡張メソッドはすべて値を返すので、それらを連鎖させることができます。
collection.Where(i => i.Name = "hello").Select(i => i.FullName);
ただし、LINQ が拡張メソッドを使用して実装されているからといって、拡張メソッドを同じように使用して値を返す必要があるわけではありません。値を返さない一般的な機能を公開する拡張メソッドを作成することは、完全に有効な使用方法です。
ForEach に関する特定の議論は、拡張メソッドの制約 (つまり、拡張メソッドが同じシグネチャを持つ継承されたメソッドをオーバーライドしないということ) に基づいて、要素を実装するすべてのクラスでカスタム拡張メソッドが使用できる状況が発生する可能性があるということです。 IEnumerable > List > を除く。これは、拡張メソッドまたは継承メソッドが呼び出されているかどうかに応じてメソッドの動作が異なり始めると、混乱を招く可能性があります。<T
<T
(連鎖可能ですが、遅延評価された)を使用してSelect
、最初に操作を実行し、次にID(または必要に応じて他の何か)を返すことができます。
IEnumerable<string> people = new List<string>(){"alica", "bob", "john", "pete"};
people.Select(p => { Console.WriteLine(p); return p; });
Count()
(afaikを列挙するための最も安価な操作)またはとにかく必要な別の操作のいずれかを使用して、それがまだ評価されていることを確認する必要があります。
私はそれが標準ライブラリに持ち込まれるのを見たいです:
static IEnumerable<T> WithLazySideEffect(this IEnumerable<T> src, Action<T> action) {
return src.Select(i => { action(i); return i; } );
}
上記のコードpeople.WithLazySideEffect(p => Console.WriteLine(p))
は、実質的にforeachと同等ですが、怠惰で連鎖可能になります。
@コインコイン
foreach 拡張メソッドの真価は、Action<>
不要なメソッドをコードに追加せずに再利用できることにあります。10 個のリストがあり、それらに対して同じロジックを実行したいとします。対応する関数はクラスに適合せず、再利用されません。10 個の for ループ、または明らかに属さないヘルパーである汎用関数を使用する代わりに、すべてのロジックを 1 か所に保持できます ( Action<>
.
Action<blah,blah> f = { foo };
List1.ForEach(p => f(p))
List2.ForEach(p => f(p))
等...
ロジックは 1 か所にあり、クラスを汚染していません。
ほとんどの LINQ 拡張メソッドは結果を返します。ForEach は何も返さないため、このパターンには適合しません。
F# (.NET の次のバージョンに含まれる予定) を使用している場合は、次を使用できます。
Seq.iter doSomething myIEnumerable
何かを返したいときにselectを使うことができます。そうでない場合は、コレクション内の何も変更したくないため、最初にToListを使用できます。
それは私ですか、List<T>.Foreach は Linq によってほとんど廃止されました。もともとあった
foreach(X x in Y)
ここで、Y は単に IEnumerable (Pre 2.0) であり、GetEnumerator() を実装する必要がありました。生成された MSIL を見ると、まったく同じであることがわかります。
IEnumerator<int> enumerator = list.GetEnumerator();
while (enumerator.MoveNext())
{
int i = enumerator.Current;
Console.WriteLine(i);
}
( MSIL については、http: //alski.net/post/0a-for-foreach-forFirst-forLast0a-0a-.aspx を参照してください)
その後、DotNet2.0 で Generics と List が登場しました。Foreach は常に、Vistor パターンの実装であると私は感じていました (Gamma、Helm、Johnson、Vlissides によるデザイン パターンを参照してください)。
もちろん、3.5 では代わりに Lambda を使用して同じ効果を得ることができ ます。私の/
私はそれについてブログ投稿を書きました: http://blogs.msdn.com/kirillosenkov/archive/2009/01/31/foreach.aspx
.NET 4.0 でこのメソッドを確認したい場合は、ここで投票できます: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=279093
3.5 では、IEnumerable に追加されたすべての拡張メソッドが LINQ サポートのために存在します (System.Linq.Enumerable クラスで定義されていることに注意してください)。この記事では、foreach が LINQ に属さない理由を説明し ます。
Akuの答えを拡張したいと思います。
最初に列挙可能な全体を反復せずに、副作用のためだけにメソッドを呼び出したい場合は、これを使用できます。
private static IEnumerable<T> ForEach<T>(IEnumerable<T> xs, Action<T> f) {
foreach (var x in xs) {
f(x); yield return x;
}
}
ForEach<T> を使用すると、foreach キーワードが実行時にチェックされるコンパイル時に型チェックが行われることを指摘した人はまだ誰もいません。
コードで両方のメソッドが使用されている場所でいくつかのリファクタリングを行った後、foreach の問題を見つけるためにテストの失敗/ランタイムの失敗を探し出す必要があったため、.ForEach を好みます。