Foreach Loop VS For Loop

by adrian vintu 4/23/2008 11:42:00 PM

What do you suppose happens when this piece of code is run? Please take notice that the secondary foreach loop is using the same dataset.

DataSet dsMain = GetMainDataset();

foreach (DataRow dr in dsMain.Tables[0].Rows)
{
    Console.WriteLine(dr["ID"] + " | " + dr["NAME"]);

    dsMain = GetSecondaryDataset();

    foreach (DataRow drSecondary in dsMain.Tables[0].Rows)
        Console.WriteLine(drSecondary["DETAIL"]);
}

Will it generate a run time error? Infinite loop? Half the results? Is the DataRow variable dr equal to drSecondary? Is dsMain the same all over the code?

And, of course, WHY?


Well, let's take it step by step.


We first step through the first foreach loop and notice the dsMain pointer is 15335436 - badly shown in the photos in the watch.


The dataset dsMain contains 2 columns: ID and NAME.


We step after the

dsMain = GetSecondaryDataset();

and see that the pointer has changed to 15400908 - the assignment was successful and we have a new dataset.


The dataset dsMain now contains one column called DETAIL.


At this point we can raise the question: what happens after this inner foreach finishes and the loop goes back to the outer one?

Will the outer foreach loop end because the rows of the dataset were run to the end by the inner foreach loop?

Will the outer foreach loop crash because 

Console.WriteLine(dr["ID"] + " | " + dr["NAME"]);

is looking for columns ID and NAME that don't exist in dsMain dataset - remember the previous step where dsMain became a new dataset, with only one column DETAIL?


In order to answer our question, we need to look at the disassembly.

 


Did you catch the idea? The foreach loops are not using direct pointer to the dataset dsMain rows collection, but rather pointers to the enumerator to the rows collection. And the enumerator enumerates through the collection regardless of what happens to the dsMain pointer.

So the answer is: the code will work fine, because foreach loops use the (pointers to the) enumerators of the rows collection, not pointers to the rows.


Let's see what happens when we use the normal for loop.

DataSet dsMain = GetMainDataset();

for (int i = 0; i < dsMain.Tables[0].Rows.Count; i++)
{
    DataRow dr = dsMain.Tables[0].Rows[i];

    Console.WriteLine(dr["ID"] + " | " + dr["NAME"]);

    dsMain = GetSecondaryDataset();

    foreach (DataRow drSecondary in dsMain.Tables[0].Rows)
        Console.WriteLine(drSecondary["DETAIL"]);
}


Explained by the red highlights:

1. the address of the dataset is at index 0 - location 0

2. the dataset is built outside the outer for loop and stored at location 0 - as expected.

3. the dataset is loaded from location 0 by the for loop

4. in the for loop, just before the inner foreach loop, we create a new dataset and store it at location 0, overwriting the previous pointer - same behaviour as in the double foreach example above.

5. the for loop returns to address L_000e which is our point 3.


What happens, is that the dataset containing the DETAIL column (which is stored at location 0) is used by the for loop. The for loop will continue and try to access the second row in the dataset. This will work.

What won't work is the

Console.WriteLine(dr["ID"] + " | " + dr["NAME"]);

because the row is accessed through the location0.row[i] and this row belongs to the dataset containing the DETAIL column, not the ID and NAME. 


I hope you have enjoyed this article as much as I have, and I hope things are now clearer regarding the differences between the for and foreach loops.


As an extra piece of information, in the first example, after the code has been run 1once, and it's going the second time through the outer foreach loop, the Visual Studio 2k5 and 2k8 watch will show the contents of dsMain as the contents of the overwriting dataset i.e. the one containing the DETAIL column. This makes perfect sense actually.

You can see the contents of the ~right~ dataset/table though, by going to the dr variable in the watch and looking for the Table property.

 

PS This article is a well known/self understood for experienced developers, but I do sometimes get strange faces when asking people about the example I described.


Related posts

Comments

6/22/2008 9:51:12 AM

firefly

Any decent programmer will use two separate local var for the two for loop hence eliminate the confusion all together. This kind of coding will end up with endless hour of debug for no reason.

firefly

6/22/2008 12:01:50 PM

adrian vintu

and in a perfect world there would be no bugs...

these things happen, so it's better to explain what and why, than just to blame it on poor programming.

adrian vintu

6/22/2008 1:01:46 PM

firefly

Don't get me wrong, it's good to understand the magic behind the scene of how thing work. It make you are better programmer Smile. But that knowledge is no excuse for writing cryptic code. Bug happen so that's why it's even more important to write code that anyone can debug rather than rely on a language side effect.

firefly

6/22/2008 1:06:24 PM

adrian vintu

totally agree fireflySmile

adrian vintu

Comments are closed

About Adrian Vintu

Adrian Vintu

Adrian Vintu is an old timer who has had the wonderful experience of working in a variety of quality environments and with various quality people.

Throughout time he has got down with Assembler, C++, Borland Delphi, Java, C#, Android etc. He took part in projects concerning software security, industrial controllers, food and health-care ERPS, AI simulations, data mining, mobile development, computer games, augmented reality, online distributed transactions, and financial management and trading.

In his spare time he develops free educational applications that run on Android and Windows Phone. It is his way of thanking and giving back to the open source and free (as in beer) software community and a way of bringing value to the social community and young generation.

Send mail

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2008 - 2014

Sign in