Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does foreach evaluate the array at every iteration?

Tags:

arrays

c#

foreach

I want to create a foreach which skips the first item. I've seen elsewhere that the easiest way to do this is to use myCollection.Skip(1), but I have a question:

The MSDN documentation on .Skip() describes that it "Bypasses a specified number of elements in a sequence and then returns the remaining elements." Does this mean that a call to

foreach(object i in myCollection.Skip(1)) { ... } 

Would the program have to perform .Skip(1) every time the foreach iterates? Or does foreach (somewhat like a switch) not require multiple evaluations of the array?

Would it be more efficient to create a dummy var _dummy = myCollection.Skip(1) and iterate on this instead?

like image 610
Wasabi Avatar asked Nov 08 '13 19:11

Wasabi


People also ask

Does forEach change the original array?

forEach() does not mutate the array on which it is called.

What can forEach statements iterate through?

The foreach loop in C# iterates items in a collection, like an array or a list. It proves useful for traversing through each element in the collection and displaying them. The foreach loop is an easier and more readable alternative to for loop.

Is a forEach loop more efficient than a for loop?

The forloop is faster than the foreach loop if the array must only be accessed once per iteration.

Is forEach a performance?

Foreach performance is approximately 6 times slower than FOR / FOREACH performance. The FOR loop without length caching works 3 times slower on lists, comparing to arrays. The FOR loop with length caching works 2 times slower on lists, comparing to arrays.


1 Answers

I just mocked your code with this

foreach(var v in Enumerable.Range(1,10).Skip(1))     v.Dump(); 

And here is the IL generated.

IL_0001:  nop          IL_0002:  ldc.i4.1     IL_0003:  ldc.i4.s    0A  IL_0005:  call        System.Linq.Enumerable.Range IL_000A:  ldc.i4.1     IL_000B:  call        System.Linq.Enumerable.Skip//Call to Skip IL_0010:  callvirt    System.Collections.Generic.IEnumerable<System.Int32>.GetEnumerator IL_0015:  stloc.1     // CS$5$0000 IL_0016:  br.s        IL_0026 IL_0018:  ldloc.1     // CS$5$0000 IL_0019:  callvirt    System.Collections.Generic.IEnumerator<System.Int32>.get_Current IL_001E:  stloc.0     // v IL_001F:  ldloc.0     // v IL_0020:  call        LINQPad.Extensions.Dump IL_0025:  pop          IL_0026:  ldloc.1     // CS$5$0000 IL_0027:  callvirt    System.Collections.IEnumerator.MoveNext IL_002C:  stloc.2     // CS$4$0001 IL_002D:  ldloc.2     // CS$4$0001 IL_002E:  brtrue.s    IL_0018 IL_0030:  leave.s     IL_0042 IL_0032:  ldloc.1     // CS$5$0000 IL_0033:  ldnull       IL_0034:  ceq          IL_0036:  stloc.2     // CS$4$0001 IL_0037:  ldloc.2     // CS$4$0001 IL_0038:  brtrue.s    IL_0041 IL_003A:  ldloc.1     // CS$5$0000 IL_003B:  callvirt    System.IDisposable.Dispose IL_0040:  nop          IL_0041:  endfinally   

As you can see Skip is called only once.

Equivalent c# code would look something like this

IEnumerator<int> e = ((IEnumerable<int>)values).GetEnumerator();//Get the enumerator try {   int m;//This variable is here prior to c#5.0   while(e.MoveNext())   {//int m; is declared here starting from c#5.0     m = (int)(int)e.Current;     //Your code here   } } finally {   if (e != null) ((IDisposable)e).Dispose(); } 

Consider the below code, If foreach calls VeryLongRunningMethodThatReturnsEnumerable at each iteration then that would be nightmare. Huge flaw in the design of the language. Fortunately it doesn't do that.

foreach(var obj in VeryLongRunningMethodThatReturnsEnumerable()) {    //Do something with that obj } 
like image 78
Sriram Sakthivel Avatar answered Oct 05 '22 21:10

Sriram Sakthivel