web-dev-qa-db-ja.com

IEnumerable <T>に対するforeachと同等のLINQ

LINQで次のようなことをしたいのですが、どうすればよいかわかりません。

IEnumerable<Item> items = GetItems();
items.ForEach(i => i.DoStuff());

本当の構文は何ですか?

663
tags2k

IEnumerableにはForEach拡張はありません。 List<T>専用です。だからあなたはできる

items.ToList().ForEach(i => i.DoStuff());

あるいは、独自のForEach拡張メソッドを書きます。

public static void ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
{
    foreach(T item in enumeration)
    {
        action(item);
    }
}
796
Fredrik Kalseth

Fredrikがこの修正を提供しましたが、なぜこれが最初からフレームワークに含まれていないのかを検討する価値があります。私は、LINQクエリ演算子は副作用のない、世界を見るための合理的に機能的な方法に適合するべきであると考えています。明らかにForEachは正反対です - 純粋に 副作用ベースの構文。

これは悪いことではありません。決定の背後にある哲学的な理由について考えるだけです。

342
Jon Skeet

Update 7/17/2012:C#5.0から、下記のforeachの振る舞いが変更され、 " 入れ子になったラムダ式でforeach反復変数を使用しても、予期しない結果が生じることはなくなりました。 "この答えはC#≧5.0には適用されません。

@John Skeetとforeachキーワードを好む人全員。

C#5.0より前の "foreach"の問題は、他の言語での "for forhension"と同等の動作と、それがどのように動作すると予想されるかとが矛盾することです。他の人が読みやすさに関する意見を述べているからといってここで述べています。 「 変更されたクロージャへのアクセス 」および「 ループ変数を閉じることは有害と見なされる 」に関する質問をすべて参照してください。これは、「foreach」がC#で実装されているため、「有害」にすぎません。

次の例では、@ Fredrik Kalsethの答えにあるものと機能的に同等の拡張方法を使用しています。

public static class Enumerables
{
    public static void ForEach<T>(this IEnumerable<T> @this, Action<T> action)
    {
        foreach (T item in @this)
        {
            action(item);
        }
    }
}

あまりにも工夫を凝らした例に対する謝罪。 Observableを使っているのは、このようなことをするために完全には取り込まれていないからです。明らかに、このオブザーバブルを作成するためのより良い方法があります、私はポイントを実証しようとしているだけです。通常、オブザーバブルをサブスクライブしているコードは非同期的に、場合によっては別のスレッドで実行されます。 「foreach」を使用すると、これは非常に奇妙で潜在的に非決定的な結果を生み出す可能性があります。

"ForEach"拡張メソッドを使用した次のテストに合格します。

[Test]
public void ForEachExtensionWin()
{
    //Yes, I know there is an Observable.Range.
    var values = Enumerable.Range(0, 10);

    var observable = Observable.Create<Func<int>>(source =>
                            {
                                values.ForEach(value => 
                                    source.OnNext(() => value));

                                source.OnCompleted();
                                return () => { };
                            });

    //Simulate subscribing and evaluating Funcs
    var evaluatedObservable = observable.ToEnumerable().Select(func => func()).ToList();

    //Win
    Assert.That(evaluatedObservable, 
        Is.EquivalentTo(values.ToList()));
}

以下はエラーで失敗します。

予想される値:<0、1、2、3、4、5、6、7、8、9>と同等。ただし<9、9、9、9、9、9、9、9、9、 9>

[Test]
public void ForEachKeywordFail()
{
    //Yes, I know there is an Observable.Range.
    var values = Enumerable.Range(0, 10);

    var observable = Observable.Create<Func<int>>(source =>
                            {
                                foreach (var value in values)
                                {
                                    //If you have resharper, notice the warning
                                    source.OnNext(() => value);
                                }
                                source.OnCompleted();
                                return () => { };
                            });

    //Simulate subscribing and evaluating Funcs
    var evaluatedObservable = observable.ToEnumerable().Select(func => func()).ToList();

    //Fail
    Assert.That(evaluatedObservable, 
        Is.EquivalentTo(values.ToList()));
}
36
drstevens

IEnumerable<T>で利用可能なFirstOrDefault()拡張子を使うことができます。述語からfalseを返すことで、それは各要素に対して実行されますが、実際には一致が見つからないことを気にすることはありません。これはToList()のオーバーヘッドを避けます。

IEnumerable<Item> items = GetItems();
items.FirstOrDefault(i => { i.DoStuff(); return false; });
34
Rhames

私はFredrikの方法を取り、戻り型を修正しました。

このように、このメソッドは他のLINQメソッドと同様に 遅延実行 をサポートします。

EDIT:これが明確でなかった場合、このメソッドの使用方法はToList()で終わらなければなりません。そうでなければ、アクションは実行されません。

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

そして、これを確認するためのテストがあります。

[Test]
public void TestDefferedExecutionOfIEnumerableForEach()
{
    IEnumerable<char> enumerable = new[] {'a', 'b', 'c'};

    var sb = new StringBuilder();

    enumerable
        .ForEach(c => sb.Append("1"))
        .ForEach(c => sb.Append("2"))
        .ToList();

    Assert.That(sb.ToString(), Is.EqualTo("121212"));
}

最後にToList()を削除すると、StringBuilderに空の文字列が含まれているため、テストが失敗したことになります。これは、どのメソッドもForEachに列挙を強制しなかったためです。

20
Dor Rotman

私のIEnumerableからあなたの副作用を避けなさい

LINQで次のようなことをしたいのですが、どうすればよいかわかりません。

他の人が指摘したように ここ そして海外ではLINQとIEnumerableメソッドは副作用がないと期待されています。

あなたは本当にIEnumerableの各項目に「何かをしたい」のですか?それならforeachが最良の選択です。ここで副作用が発生しても人々は驚かない。

foreach (var i in items) i.DoStuff();

あなたは副作用が欲しくないと思います

しかし、私の経験では副作用は通常必要ありません。たいていの場合、Jon Skeet、Eric Lippert、またはMarc GravellによるStackOverflow.comの回答を伴って、単純なLINQクエリが発見されるのを待っているのです。

いくつかの例

実際に単に値を集計(累積)しているだけの場合は、Aggregate拡張メソッドを検討する必要があります。

items.Aggregate(initial, (acc, x) => ComputeAccumulatedValue(acc, x));

おそらく、既存の値から新しいIEnumerableを作成したいでしょう。

items.Select(x => Transform(x));

あるいは、ルックアップテーブルを作成したいかもしれません。

items.ToLookup(x, x => GetTheKey(x))

可能性のリスト(完全に意図されているわけではない)は延々と続く。

18
cdiggins

あなたが列挙ロールとして振る舞いたいのであれば、あなたはそれぞれのアイテムを生み出すべきです。

public static class EnumerableExtensions
{
    public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
    {
        foreach (var item in enumeration)
        {
            action(item);
            yield return item;
        }
    }
}
11
regisbsb

Interactive Extensions to LINQNuGet上RxTeamsのプロファイル 参照)のMicrosoftによる実験的リリースがあります。 チャンネル9ビデオ はそれをよく説明しています。

そのドキュメントはXMLフォーマットでのみ提供されています。読みやすくするために、これを実行しました Sandcastleのドキュメント docsアーカ​​イブを解凍し、 index.html を探します。

他の多くの優れたものの中で、それは期待されたForEach実装を提供します。それはあなたがこのようなコードを書くことを可能にします:

int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8 };

numbers.ForEach(x => Console.WriteLine(x*x));
10
John Wigger

PLINQ(.Net 4.0以降で利用可能)によると、

IEnumerable<T>.AsParallel().ForAll() 

iEnumerableで並列foreachループを実行します。

8
Wolf5

ForEachの目的は副作用を引き起こすことです。 IEnumerableはセットの遅延列挙のためのものです。

この概念上の違いは、あなたがそれを考えたときにはっきりと見えます。

SomeEnumerable.ForEach(item=>DataStore.Synchronize(item));

これは、 "count"や "ToList()"、あるいはそれに対して何かをするまで実行されません。それは明らかに表現されたものではありません。

IEnumerable拡張機能を使用して、一連の反復を設定し、それぞれのソースと条件でコンテンツを定義します。 Expression Treeは強力で効率的ですが、その性質を理解することを学ぶべきです。そして怠惰な評価を無効にして少数の文字を節約するためにそれらのまわりでプログラミングするためだけではない。

6
Tormod

多くの人がそれを述べました、しかし私はそれを書き留めなければなりませんでした。これは最も明瞭で読みやすいものではないでしょうか。

IEnumerable<Item> items = GetItems();
foreach (var item in items) item.DoStuff();

短くてシンプル(st)。

5
Nenad

多くの答えがすでに指摘しているように、あなたは簡単にあなた自身でそのような拡張メソッドを追加することができます。しかし、そうしたくないのであれば、BCLにはこのようなことは何もありませんが、 Reactive Extension への参照がある場合は、Systemname__名前空間にまだオプションがあります。 (あなたがそうでなければ、あなたは持っているべきです):

using System.Reactive.Linq;

items.ToObservable().Subscribe(i => i.DoStuff());

メソッド名は少し異なりますが、最終的な結果はまさにあなたが探しているものです。

5
Mark Seemann

今、我々はの選択肢があります...

        ParallelOptions parallelOptions = new ParallelOptions();
        parallelOptions.MaxDegreeOfParallelism = 4;
#if DEBUG
        parallelOptions.MaxDegreeOfParallelism = 1;
#endif
        Parallel.ForEach(bookIdList, parallelOptions, bookID => UpdateStockCount(bookID));

もちろん、これはまったく新しいスレッドワームの可能性を開きます。

ps(フォントについては申し訳ありません。システムが決定したものです)

3
Paulustrious

ForEachはChainedでも構いません。アクションの後にパイルラインに戻すだけです。 流暢なまま


Employees.ForEach(e=>e.Act_A)
         .ForEach(e=>e.Act_B)
         .ForEach(e=>e.Act_C);

Orders  //just for demo
    .ForEach(o=> o.EmailBuyer() )
    .ForEach(o=> o.ProcessBilling() )
    .ForEach(o=> o.ProcessShipping());


//conditional
Employees
    .ForEach(e=> {  if(e.Salary<1000) e.Raise(0.10);})
    .ForEach(e=> {  if(e.Age   >70  ) e.Retire();});

実装のEagerバージョン。

public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enu, Action<T> action)
{
    foreach (T item in enu) action(item);
    return enu; // make action Chainable/Fluent
}

Edit: a Lazyバージョンは、 this のようにyield returnを使用しています。

public static IEnumerable<T> ForEachLazy<T>(this IEnumerable<T> enu, Action<T> action)
{
    foreach (var item in enu)
    {
        action(item);
        yield return item;
    }
}

Tozy()など、具体化されるレイジーバージョンNEEDs、それ以外の場合は何も起こりません。以下のToolmakerSteveからの素晴らしいコメントを参照してください。

IQueryable<Product> query = Products.Where(...);
query.ForEachLazy(t => t.Price = t.Price + 1.00)
    .ToList(); //without this line, below SubmitChanges() does nothing.
SubmitChanges();

私は自分のライブラリにForEach()とForEachLazy()の両方を入れています。

2
Rm558

Jon Skeetに触発されて、私は彼の解決策を次のように拡張しました。

拡張方法:

public static void Execute<TSource, TKey>(this IEnumerable<TSource> source, Action<TKey> applyBehavior, Func<TSource, TKey> keySelector)
{
    foreach (var item in source)
    {
        var target = keySelector(item);
        applyBehavior(target);
    }
}

クライアント:

var jobs = new List<Job>() 
    { 
        new Job { Id = "XAML Developer" }, 
        new Job { Id = "Assassin" }, 
        new Job { Id = "Narco Trafficker" }
    };

jobs.Execute(ApplyFilter, j => j.Id);

。 。 。

    public void ApplyFilter(string filterId)
    {
        Debug.WriteLine(filterId);
    }
2
Scott Nimrod

私は尊重していますが、リンクエクステンションメソッドは副作用のないものにすべきだという考えには賛成できません(そうではないという理由だけでなく、どのデリゲートも副作用を実行できるのです)。

次の点を考慮してください。

   public class Element {}

   public Enum ProcessType
   {
      This = 0, That = 1, SomethingElse = 2
   }

   public class Class1
   {
      private Dictionary<ProcessType, Action<Element>> actions = 
         new Dictionary<ProcessType,Action<Element>>();

      public Class1()
      {
         actions.Add( ProcessType.This, DoThis );
         actions.Add( ProcessType.That, DoThat );
         actions.Add( ProcessType.SomethingElse, DoSomethingElse );
      }

      // Element actions:

      // This example defines 3 distict actions
      // that can be applied to individual elements,
      // But for the sake of the argument, make
      // no assumption about how many distict
      // actions there may, and that there could
      // possibly be many more.

      public void DoThis( Element element )
      {
         // Do something to element
      }

      public void DoThat( Element element )
      {
         // Do something to element
      }

      public void DoSomethingElse( Element element )
      {
         // Do something to element
      }

      public void Apply( ProcessType processType, IEnumerable<Element> elements )
      {
         Action<Element> action = null;
         if( ! actions.TryGetValue( processType, out action ) )
            throw new ArgumentException("processType");
         foreach( element in elements ) 
            action(element);
      }
   }

この例が示すものは、アクションを定義し変換する値をデコードするための大きなswitch構造を記述する必要なしに、一連の要素に副作用を持つ可能なアクションの1つを呼び出すことを可能にする一種の遅延バインディングです。対応するメソッドに入れてください。

1
caddzooks

MoreLinqにはIEnumerable<T>.ForEachとその他多数の便利な拡張機能があります。おそらくForEachのためだけに依存関係を取る価値はありませんが、そこにはたくさんの役に立つものがあります。

https://www.nuget.org/packages/morelinq/

https://github.com/morelinq/MoreLINQ

1
solublefish

流暢さを保つために、そのようなトリックを使うことができます。

GetItems()
    .Select(i => new Action(i.DoStuf)))
    .Aggregate((a, b) => a + b)
    .Invoke();
1
Alex

VB.NETのためにあなたは使うべきです:

listVariable.ForEach(Sub(i) i.Property = "Value")
1

さらに別のForEachの例

public static IList<AddressEntry> MapToDomain(IList<AddressModel> addresses)
{
    var workingAddresses = new List<AddressEntry>();

    addresses.Select(a => a).ToList().ForEach(a => workingAddresses.Add(AddressModelMapper.MapToDomain(a)));

    return workingAddresses;
}
1
neil martin

この「機能的アプローチ」の抽象化は大きな時間をかけます。言語レベルでは副作用を防ぐものは何もありません。コンテナ内のすべての要素に対してラムダ/デリゲートを呼び出すことができる限り、 "ForEach"の動作が得られます。

例えば、srcDictionaryをdestDictionaryにマージする1つの方法(キーが既に存在する場合は上書き)

これはハックであり、いかなるプロダクションコードでも使うべきではありません。

var b = srcDictionary.Select(
                             x=>
                                {
                                  destDictionary[x.Key] = x.Value;
                                  return true;
                                }
                             ).Count();
1
Zar Shardan