44

重複の可能性:
IEnumerable<T> の foreach に相当する LINQ

List<T>ForEach各要素に対して渡されたアクションを実行するメソッドが呼び出されます。

var names = new List<String>{ "Bruce", "Alfred", "Tim", "Richard" };

names.ForEach(p =>  { Console.WriteLine(p); });

しかし、namesが ではなく の場合はどうList<T>なるIList<T>でしょうか。IList<T>のようなメソッドはありませんForEach

代替手段はありますか?

4

4 に答える 4

48

foreachループを使用します。

foreach (var p in names) {
    Console.WriteLine(p);
}

実際に可読性が向上しないのであれば、デリゲートと拡張メソッドをあちこちで使用する理由はありません。ループは、foreach何が行われているかをForEachメソッドよりも明確に読者に伝えます。

于 2012-12-19T06:41:11.987 に答える
24

IList<T>が配列 ( T[])の場合、onと同様にArray.ForEachメソッドがあります。カスタムまたは好みの拡張メソッドを作成できます。ForEachList<T>IList<T>IEnumerable<T>

public static void ForEach<T>(this IList<T> list, Action<T> action)
{
    foreach (T t in list)
        action(t);
}

元のコレクションのオブジェクトが変更されるという事実に注意する必要がありますが、命名はそれを暗示していると思います.

-------------------------------------------------- -------------------------------------------------- --------------------------------

私は電話することを好みます:

people.Where(p => p.Tenure > 5)
      .Select(p => p.Nationality)
      .ForEach(n => AssignCitizenShip(n);

よりも

foreach (var n in people.Where(p => p.Tenure > 5).Select(p => p.Nationality))
{
    AssignCitizenShip(n);
}

もしそうなら、で拡張メソッドを作成できますIEnumerable。終了呼び出しがクエリForEachを実行することに注意してください。Linq必要ない場合は、yieldステートメントを使用してIEnumerable<T>backを返すことで延期することもできます。

public static IEnumerable<T> ForEach<T>(this IEnumerable<T> list, Action<T> action)
{
    foreach (T t in list)
    {
        action(t);
        yield return t;
    }
}

これで副作用の問題は解決しますが、個人的には、ForEach最終的に呼び出しを実行するという名前のメソッドが好きです。

-------------------------------------------------- -------------------------------------------------- -------------------------------

好みに関する反対意見に対処するために、これよりも優れた Eric Lippert からのリンクを次に示します。彼を引用するには:

「最初の理由は、そうすることは、他のすべてのシーケンス演算子が基づいている関数型プログラミングの原則に違反することです。明らかに、このメソッドへの呼び出しの唯一の目的は、副作用を引き起こすことです。式の目的は、値を計算することです。副作用を引き起こさない. ステートメントの目的は副作用を引き起こすことです. このことの呼び出し場所は式に非常によく似ています. 「ステートメント式」のコンテキストで使用する必要があります。) 副作用にのみ役立つ唯一無二のシーケンス演算子を作成するのは、私には合いません。

2 番目の理由は、そうすることで言語に新たな表現力がまったく追加されないことです。」

エリックは、それが悪いことだと言っているのではありません。デフォルトで構成を含めないという決定の背後にある哲学的な理由にすぎません。Linqの関数がコンテンツに作用するべきではないと思われる場合は、IEnumerable実行しないでください。私はそれが何をするかをよく知っているので、個人的には気にしません。コレクションクラスに副作用を引き起こす他のメソッドとして扱います。必要に応じて、関数に入ってデバッグすることもできます。これはそれ自体からの別のものLinqです。

people.Where(p => p.Tenure > 5)
      .Select(p => p.Nationality)
      .AsParallel()
      .ForAll(n => AssignCitizenShip(n);

私が言うように、これらについて悪いことは何もありません。個人的な好みです。ネストされた s に対して、またはループ内で実行するコードが複数行含まれている場合は、これを使用しません。foreachforeachしかし、私が投稿した簡単な例については、気に入っています。すっきりと簡潔に見えます。

編集:パフォーマンス リンクを参照してください: List<T>.ForEach が標準の foreach よりも速いのはなぜですか?

于 2012-12-19T06:35:08.613 に答える
8

拡張メソッドを作成して、 の実装のほとんどを使用できますvoid List<T>.ForEach(Action<T> action)。ソース コードは、Shared Source Initiativeサイトからダウンロードできます。

基本的には、次のように終了します。

public static void ForEach<T>(this IList<T> list, Action<T> action) 
{
    if (list == null) throw new ArgumentNullException("null");
    if (action == null) throw new ArgumentNullException("action");

    for (int i = 0; i < list.Count; i++)
    {
        action(list[i]);
    }
}

foreachIList にインデクサーが含まれているという事実を利用しているため、このステートメントを使用する他の実装よりもわずかに優れています。

OR Mapper の回答には同意しますが、多くの開発者が参加する大規模なプロジェクトでは、foreachステートメントがより明確であると全員に納得させるのが難しい場合があります。さらに悪いことに、API が具象型 (List) ではなくインターフェイス (IList) に基づいている場合、慣れている開発者はIList 参照List<T>.ForEach methodを呼び出し始める可能性があります。ToList以前のプロジェクトで起こったので知っています。Framework Design Guidelinesに従って、パブリック API のあらゆる場所でコレクション インターフェイスを使用していました。これに慣れていない多くの開発者がToList驚くべき速さで出現し始めたことに気付くまでにしばらく時間がかかりました。最後に、この拡張メソッドを、誰もが使用している共通のアセンブリに追加し、すべての不要な呼び出しが確実に行われるようにしました。ToListコードベースから削除されました。

于 2012-12-20T12:57:30.247 に答える
3

このコードを静的クラスに追加して、拡張機能と呼びます。

public static void ForEach<T>(this IList<T> list, Action<T> action) {
    foreach(var item in list) {
        action.Invoke(item);
    }
}
于 2012-12-19T06:35:45.517 に答える