2009-07-23 13 views
3

どちらのアプローチにも利点はありますか? Listアイテムをトラバースしてそれぞれのアクションを実行する必要がある場合は、従来のforeachループメカニズムを使用するか、List.ForEachに移動する必要がありますか?いつリスト<T> .ForEachをネイティブforeachループで使用しますか?

Matthew Podwysocki @ CodeBetter.comは、anti-for campaignについての興味深い記事を書いています。これはループが解決しようとしている問題について私に考えさせました。この記事では、Matthewは明示的なループ構造によって、「何か」の代わりに「方法」について考えるようになると主張しています。

(もしあれば)他のものを重ねて使う良い理由は何ですか?

答えて

9

代理人が何らかの理由で申し込みを受けた場合は、それを使用します。たとえば、独自のリストを作成し、それを作成し、各エントリにデリゲートを適用することができます。その時点で、書き込み:

list.ForEach(action); 

は、私はList.ForEachが大幅に高速であることが判明

foreach (Item item in list) 
{ 
    action(item); 
} 
2

.ForEach()に渡す既存の代理人がいる場合にのみ、私は利点があります。

他のほとんどのケースでは、実際のforeach()を使用する方が効率的で読みやすいと思います。

+0

本当にですか?以下のCIL出力とパフォーマンスベンチマークを参照してください。 –

+1

私はこれらのベンチマークの精度を疑う。サンプルが小さすぎ、テストケースが現実のコード(IMHO)を表していない –

+0

より現実的なコードで更新されました。 List.ForEachメソッドは依然として大幅に高速です。テストをより有効にする提案がある場合は、私に知らせてください。 –

1

私はMatthew Podwysockiに同意します。デリゲートを渡していない限り、それらのうちの1つを使用してコレクションをループしたい場合は、標準のループ構造に固執します。

8

よりも簡単です。ここでは(今改訂版)の性能試験の最後の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 

各テストは億(100,000,000)の反復で実行されました。カスタムクラス(Fruit)を使用するようにテストを更新し、各ループにアクセスして、現在のオブジェクトの内部のメンバーと作業するようにしました。各ループは同じタスクを実行しています。ここ

は、テストクラスの全体のソースである:ここ

class ForEachVsClass 
{ 

static Int32 Iterations = 1000000000; 
static int Work = 0; 

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

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


internal class Fruit { 
    public string Name { get; set; } 
    public int Value { get; set; } 
    public Fruit(string _Name, int _Value) { Name = _Name; Value = _Value; } 
} 


[Benchmark] 
public static void NativeForLoop() 
{ 
    for (int x = 0; x < Iterations; x++) 
    { 
     NativeForLoopWork(); 
    } 

} 

public static void NativeForLoopWork() 
{ 
    foreach (Fruit CurrentFruit in ListOfFruit) { 
     Work+=CurrentFruit.Value; 
    } 
} 


[Benchmark] 
public static void ListDotForEach() 
{ 
    for (int x = 0; x < Iterations; 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] class ForEachVsClass/Fruit CurrentFruit, 
     [1] valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<class ForEachVsClass/Fruit> CS$5$0000) 
    L_0000: ldsfld class [mscorlib]System.Collections.Generic.List`1<class ForEachVsClass/Fruit> ForEachVsClass::ListOfFruit 
    L_0005: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> [mscorlib]System.Collections.Generic.List`1<class ForEachVsClass/Fruit>::GetEnumerator() 
    L_000a: stloc.1 
    L_000b: br.s L_0026 
    L_000d: ldloca.s CS$5$0000 
    L_000f: call instance !0 [mscorlib]System.Collections.Generic.List`1/Enumerator<class ForEachVsClass/Fruit>::get_Current() 
    L_0014: stloc.0 
    L_0015: ldsfld int32 ForEachVsClass::Work 
    L_001a: ldloc.0 
    L_001b: callvirt instance int32 ForEachVsClass/Fruit::get_Value() 
    L_0020: add 
    L_0021: stsfld int32 ForEachVsClass::Work 
    L_0026: ldloca.s CS$5$0000 
    L_0028: call instance bool [mscorlib]System.Collections.Generic.List`1/Enumerator<class ForEachVsClass/Fruit>::MoveNext() 
    L_002d: brtrue.s L_000d 
    L_002f: leave.s L_003f 
    L_0031: ldloca.s CS$5$0000 
    L_0033: constrained [mscorlib]System.Collections.Generic.List`1/Enumerator<class ForEachVsClass/Fruit> 
    L_0039: callvirt instance void [mscorlib]System.IDisposable::Dispose() 
    L_003e: endfinally 
    L_003f: ret 
    .try L_000b to L_0031 finally handler L_0031 to L_003f 
} 



.method public hidebysig static void ListDotForEachWork() cil managed 
{ 
    .maxstack 8 
    L_0000: ldsfld class [mscorlib]System.Collections.Generic.List`1<class ForEachVsClass/Fruit> ForEachVsClass::ListOfFruit 
    L_0005: ldsfld class [mscorlib]System.Action`1<class ForEachVsClass/Fruit> ForEachVsClass::CS$<>9__CachedAnonymousMethodDelegate1 
    L_000a: brtrue.s L_001d 
    L_000c: ldnull 
    L_000d: ldftn void ForEachVsClass::<ListDotForEachWork>b__0(class ForEachVsClass/Fruit) 
    L_0013: newobj instance void [mscorlib]System.Action`1<class ForEachVsClass/Fruit>::.ctor(object, native int) 
    L_0018: stsfld class [mscorlib]System.Action`1<class ForEachVsClass/Fruit> ForEachVsClass::CS$<>9__CachedAnonymousMethodDelegate1 
    L_001d: ldsfld class [mscorlib]System.Action`1<class ForEachVsClass/Fruit> ForEachVsClass::CS$<>9__CachedAnonymousMethodDelegate1 
    L_0022: callvirt instance void [mscorlib]System.Collections.Generic.List`1<class ForEachVsClass/Fruit>::ForEach(class [mscorlib]System.Action`1<!0>) 
    L_0027: ret 
} 
+0

大きなリストの結果がどうなるか不思議です。リストには4つのアイテムしか含まれていません。 (btw:テストケースを変更していただきありがとうございます) –

2

Eric Lippert has come out against IEnumerable.ForEach()と私は議論の両面を見ることができます。彼の議論を脇に押し付けて実装したので、私はいくつかのコードブロックを簡潔かつ読みやすい方法で少し喜んで見つけました。

私は通常LINQについて考える必要のない副作用で噛まれてしまったので、なぜ彼がそれを出荷していないというケースを作ったのかも分かります。

代理人のケースはForEach()の方が強力ですが、私は標準的なforeachループがそれほど目立たないとは思いません。

私は確かに正しいか間違った答えはないと思います。

0

ラムダ式の読みやすいコードを.ForEach()に置くことができます。また、反復処理している型を明示的に書き出す必要もありません。強く型付けされているので、コンパイラはすでに知っています。

例:

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

ラムダ式部分に同意しましたが、書き込みを停止するものはありません。foreach(var log in logs) –

+0

true。 1つのことをする2つの方法だけです。しかし、デリゲートを使うこともできますし、コードも少なくなります。 – mkchandler

0

List.ForEachの問題は、それは属性が裏返しrefまたは渡すことはできませんです。 他のほとんどの場合、List.ForEachを使用すると読みやすくなります。

関連する問題