c# - IEnumerableにForEach拡張メソッドがないのはなぜですか?




.net vb.net (14)

3.5では、IEnumerableに追加されたすべての拡張メソッドがLINQサポート用に用意されています(System.Linq.Enumerableクラスで定義されていることに注意してください)。 この記事では、foreachがLINQに属していない理由を説明します: Parallel.Forに似た既存のLINQ拡張メソッド?

不足しているZip関数について質問する別の質問に触発された:

EnumerableクラスにForEach拡張メソッドがないのはなぜですか? それともどこですか? ForEachメソッドを取得する唯一のクラスはList<>です。 それが欠けている理由はありますか(パフォーマンス)ですか?


@コインコイン

foreach拡張メソッドの真のパワーは、あなたのコードに不要なメソッドを追加することなくAction<>を再利用することを含みます。 リストが10個あり、同じロジックを実行したいとします。対応する関数はクラスに合わず、再利用されません。 10個のforループ、または属していないヘルパーであるジェネリック関数を持つ代わりに、すべてのロジックを1つの場所に保持できます( Action<> 。したがって、数十行は

Action<blah,blah> f = { foo };

List1.ForEach(p => f(p))
List2.ForEach(p => f(p))

等...

論理は1つの場所にあり、クラスを汚染していません。


ForEach <T>は、foreachキーワードがランタイムチェックされている場所でコンパイル時の型チェックを行うことを誰も指摘していません。

両方のメソッドがコードで使用されていたリファクタリングを行っていると、私はforeachの問題を見つけるためにテストの失敗/実行時の失敗を見つけなければならなかったので、.ForEachを好む。


Foreachを使用するには、リストをプライマリメモリに読み込む必要があります。 しかし、IEnumerableの遅延読み込みの性質上、ForEachをIEnumerableに提供しません。

しかし、(ToList()を使用して)熱心な読み込みによって、リストをメモリに読み込んでForEachを利用することができます。



Akuの答えを広げたいと思います 。

最初に列挙可能な全てを反復することなく副作用のためにメソッドを呼びたい場合、これを使うことができます:

private static IEnumerable<T> ForEach<T>(IEnumerable<T> xs, Action<T> f) {
    foreach (var x in xs) {
        f(x); yield return x;
    }
}

あなたは、連鎖可能だが怠惰に評価されたSelect 、まず操作を行い、その後アイデンティティを返すことができます(または好きな場合は別のもの)

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

上記のコードは、foreach、lazy、chainableと事実上同じですが、people.WithLazySideEffect people.WithLazySideEffect(p => Console.WriteLine(p))になります。


あなたは何かを返すときにselectを使うことができます。 そうしないと、コレクション内の何も変更したくない可能性があるので、まずToListを使用できます。


したがって、ForEach拡張メソッドは、LINQ拡張メソッドのような値を返さないため、適切でないという事実について多くのコメントがありました。 これは事実ではあるが、完全に真実ではない。

LINQ拡張メソッドはすべて、チェーンでつなぐことができるように値を返します。

collection.Where(i => i.Name = "hello").Select(i => i.FullName);

ただし、LINQが拡張メソッドを使用して実装されているからといって、同じ方法で拡張メソッドを使用して値を返す必要があるわけではありません。 値を返さない共通の機能を公開する拡張メソッドを書くことは、完全に有効な使い方です。

ForEachの具体的な論点は、拡張メソッドの制約に基づいている(つまり、拡張メソッドが同じシグネチャで継承されたメソッドをオーバーライドすること決してありません)、impelementを持つすべてのクラスでカスタム拡張メソッドを使用できる場合がありますList <T >を除くIEnumerable <T > 拡張メソッドまたは継承メソッドが呼び出されているかどうかによって、メソッドが異なる動作を開始すると、混乱が生じる可能性があります。


それは私ですか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を参照してhttp://alski.net/post/0a-for-foreach-forFirst-forLast0a-0a-.aspx

その後、DotNet2.0ジェネリックスがやってきました。 Foreachは、常にVistorパターンの実装であると私に感じました(Gamma、Helm、Johnson、VlissidesによるDesign Patternsを参照)。

今度はもちろん、3.5ではラムダを同じエフェクトに使うことができます。例についてはhttp://dotnet-developments.blogs.techtarget.com/2008/09/02/iterators-lambda-and-linq-oh-my/


ほとんどの場合、ほとんどの場合、foreach文が言語に含まれています。

私は次のことを嫌う。

list.ForEach( item =>
{
    item.DoSomething();
} );

の代わりに:

foreach(Item item in list)
{
     item.DoSomething();
}

後者はほとんどの状況で読みやすく、明確になりました。

しかし、私はその問題について私の立場を変えたことを認めなければならない。 ForEach()拡張メソッドは実際にはいくつかの状況で役に立ちます。

ステートメントとメソッドの主な違いは次のとおりです。

  • 型チェック:foreachは実行時に行われ、ForEach()はコンパイル時(Big Plus!)です。
  • デリゲートを呼び出す構文は、はるかに簡単です。objects.ForEach(DoSomething);
  • ForEach()は連鎖される可能性がありますが、そのような機能の悪用/有用性は議論の余地があります。

それらはここで多くの人々によって作られたすべての素晴らしい点です、なぜ人々が機能を欠いているのか分かります。 私は、Microsoftが次のフレームワークの反復で標準の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

私はいつも自分自身を疑問に思っています。それが私がいつも私と一緒に持っている理由です。

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

ニースの小さな拡張メソッド。








extension-methods