3


0

どちらの方法にも利点はありますか? Listアイテムをトラバースしてそれぞれに対してアクションを実行する必要がある場合は、従来のforeachループメカニズムを使用するか、List.ForEachに進むか

Matthew Podwysocki @ CodeBetter.comがhttp://codebetter.com/blogs/matthew.podwysocki/archive/ 2009/06/26 / the-anti-for-campaign.aspx [anti-for campaign]について興味深い記事を書きました。 これは私がループが解決しようとしている問題について考えさせました。 この記事の中で、Matthewは、明示的なループ構造によって、 'what’ではなく 'how’について考えるようになると主張します。

一方をもう一方よりも使用するための正当な理由は何ですか?

7 Answer


9


一つの理由として、あなたが何らかの理由で申請するために代理人を渡された場合、あなたはそれを使うでしょう。 例えば、あなたはあなた自身のリストを作成し、それを追加するなどしてから各エントリにデリゲートを適用するかもしれません。 その時点で、書いている:

list.ForEach(アクション);

よりも簡単です

foreach(リスト内の項目項目){アクション(項目); }


8


私はList.ForEachがかなり速いことを発見しました。 これが(今改訂された)パフォーマンステストの最後の4回の実行の結果です。

NativeForLoop:00:00:04.7000000 ListDotForEach:00:00:02.7160000 ------------------------------------ --- NativeForLoop:00:00:04.8660000 ListDotForEach:00:00:02.6560000 --------------------------------- ------ NativeForLoop:00:00:04.6240000 ListDotForEach:00:00:02.8160000 ------------------------------ --------- NativeForLoop:00:00:04.7110000 ListDotForEach:00:00:02.7190000

各テストは1億回繰り返して実行されました。 カスタムクラス(Fruit)を使用し、各ループにアクセスして現在のオブジェクト内のメンバーを操作するようにテストを更新しました。 各ループは同じタスクを実行しています。

これがテストクラスの全ソースです。

クラスForEachVsClass {

静的Int32反復= 1000000000。 static int Work = 0;

public static void Init(string [] args){if(args.Length> 0)反復= Int32.Parse(args [0]); Console.WriteLine( "反復:"反復); }

static List ListOfFruit = new List {new Fruit( "Apple"、1)、new Fruit( "Orange"、2)、new Fruit( "Kiwi"、3)、new Fruit( "Banana"、4)};

内部クラスFruit {public string Name {get;}セット; public int値{get;}セット; public Fruit(string _Name、int _Value){名前= _名前;値= _値; }}

[Benchmark]
public static void NativeForLoop(){(int x = 0; x <反復; x){NativeForLoopWork(); }

}

Native static void NativeForLoopWork(){foreach(ListOfFruit内のFruit CurrentFruit){Work = CurrentFruit.Value; }}

[Benchmark]
public static void ListDotForEach(){(int x = 0; x <反復; x){ListDotForEachWork(); }}

public static void ListDotForEachWork(){ListOfFruit.ForEach((f)=> Work = f.Value); }

}

これが作業メソッドの結果のILです(読みやすくするために抽出されています)。

.method public hidebysig static void NativeForLoopWork() cil managed
{.maxstack 2 .locals init([0]クラスForEachVsClass / Fruit CurrentFruit、[1] valuetype [mscorlib] System.Collections.Generic.List`1 / Enumerator CS $ 5 $ 0000)L_0000:ldsfldクラス[mscorlib] System.Collections。 Generic.List`1 ForEachVsClass :: ListOfFruit L_0005:callvirtインスタンスの値のタイプ[mscorlib] System.Collections.Generic.List`1 / Enumerator [mscorlib] System.Collections.Generic.List`1 :: GetEnumerator()L_000a:stloc.1 L_000b:br.s L_0026 L_000d:ldloca.s CS $ 5 $ 0000 L_000f:インスタンスを呼び出す!0 [mscorlib] System.Collections.Generic.List`1 / Enumerator :: get_Current()L_0014:stloc.0 L_0015:ldsfld int32 ForEachVsClass: :Work L_001a:ldloc.0 L_001b:callvirtインスタンスint32 ForEachVsClass / Fruit :: get_Value()L_0020:add L_0021:stsfld int32 ForEachVsClass :: Work L_0026:ldloca.s CS $ 5 $ 0000:コールインスタンスbool [mscorlib] .Generic.List`1 / Enumerator :: MoveNext()L_002d:brtrue.s L_000d L_002f:leav es L_003f L_0031:ldloca.s CS $ 5 $ 0000 L_0033:制約あり[mscorlib] System.Collections.Generic.List`1 /列挙子L_0039:callvirtインスタンスvoid [mscorlib] System.IDisposable :: Dispose()L_003e:最後にL_003f:ret。 L_000bからL_0031へ、最後にL_0031からL_003fへのハンドラを試してみてください。


.method public hidebysig static void ListDotForEachWork() cil managed
{.maxstack 8 L_0000:ldsfldクラス[mscorlib] System.Collections.Generic.List`1 ForEachVsClass :: ListOfFruit L_0005:ldsfldクラス[mscorlib] System.Action`1 ForEachVsClass :: CS $ <> 9__CachedAnonymousMethodDelegate_ld0001 L0001 L_000c:ldnull L_000d:ldftn void ForEachVsClass :: b__0(クラスForEachVsClass / Fruit)L_0013:newobjインスタンスvoid [mscorlib] System.Action`1 ::。ctor(object、native int)L_0018:stsfldクラス[mscorlib] System.Action `1 ForEachVsClass :: CS $ <> 9__CachedAnonymousMethodDelegate1 L_001d:ldsfldクラス[mscorlib] System.Action`1 ForEachVsClass :: CS $ <> 9__CachedAnonymousMethodDelegate1 L_0022:callvirtインスタンスvoid [mscorlib] System.Collections :: System.Collections :: System.Collections :: .. (class [mscorlib] System.Action`1)L_0027:ret}


2


ForEach()に渡したい既存のデリゲートがある場合にのみ利点があります。

他のほとんどの場合、本当のforeach()を使うほうが効率的で読みやすいと思います。


2


http://blogs.msdn.com/ericlippert/archive/2009/05/18/foreach-vs-foreach.aspx[Eric LippertはIEnumerable.ForEach()に対して反対意見を述べていますが、この議論の両側を見ることができます。 彼の主張を脇に置いてそれを実行したので、私はそれがいくつかのコードブロックをどれほど簡潔にそして読みやすくするかに少々の歓喜を感じた。

私が通常LINQについて考える必要はないという副作用に悩まされてきたので、私は彼がなぜそれを同梱しないことを主張したのかを見ることもできます。

デリゲートの場合はForEach()のほうが強力ですが、標準的なforeachループがそれほど意図を覆い隠しているとは思いません。

決定的に正しいか間違った答えがあるとは思わない。


1


私はMatthew Podwysockiに同意します。 あなたがデリゲートを渡して、それらのうちの1つを使ってコレクションをループしたいのでなければ、私は標準的なループ構成に固執するでしょう。


0


ときどき、 `.ForEach()`のLambda式でコードを読みやすくすることができることがわかります。 また、繰り返している型を明示的に書き出さなくても済みます。 強く型付けされているので、コンパイラはすでに知っています。

例:

logs.ForEach(log =>
    {
        log.DoSomething();
    });


0


List.ForEachの問題は、ref属性を内部に渡したり、内部に属性を渡したりすることができないことです。 他のほとんどの場合、私はList.ForEachを使うほうが読みやすいと思います。