When will I use List <T> .ForEach through my own foreach loop?

Are there any benefits to any approach? If I need to traverse the List elements and perform an action for each of them, should I use the traditional foreach loop mechanism or go to List.ForEach?

Matthew Podwysocki @ CodeBetter.com wrote an interesting anti-campaign article. This made me think about the problem the loop is trying to solve. In this article, Matthew argues that explicit loop structures make you think of β€œhow” instead of β€œwhat.”

What are some good reasons to use one over the other (if any)?

+3
source share
7 answers

On the one hand, you would use this if a delegate handed you over to apply for any reason. For example, you can create your own list, fill it in, etc., and then apply a delegate to each entry. At this point, by writing:

list.ForEach(action); 

easier than

 foreach (Item item in list) { action(item); } 
+9
source

I found List.ForEach much faster. The following are the results of the last four runs of the test (now revised):

 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 

Each test was performed using one hundred million (100,000,000) iterations. I updated the test to use a custom class (Fruit) and got every loop access and worked with a member inside the current object. Each cycle performs the same task.

Here is the entire source of the test class:

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

}

Here is the IL result for the working methods (extracted to make them easier to read):

 .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 } 
+8
source

I see only an advantage if you have an existing delegate that you want to pass to .ForEach ().

In most other cases, I find that using real foreach () is more efficient and readable.

+2
source

Eric Lippert opposed IEnumerable.ForEach () , and I see both sides of the argument. Having cast aside his argument against him and implemented it, I found a little bit of joy in how concise and readable he made a few blocks of code.

Being bitten by side effects, which I usually should not think about in LINQ, I can also understand why he did this in order not to send it with him.

The delegate case is stronger for ForEach (), but I don't think the standard foreach loop completely hides the intent.

I do not think that there is a definitively correct or incorrect answer.

+2
source

I agree with Matthew Podwicky. If you don't pass delegates and don't want to iterate over the collection using one of them, I would stick to the standard loop constructs.

+1
source

Sometimes I find that I can get more readable code in a Lambda expression in .ForEach() . It also prevents you from explicitly writing out the type you are repeating. Since it is strongly typed, the compiler already knows.

Example:

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

The problem with List.ForEach is that it is not possible to pass ref or out attributes inside. In most other cases, I find that using List.ForEach is more readable.

0
source

Source: https://habr.com/ru/post/1380345/


All Articles