13

Possible Duplicate:
How do foreach loops work in C#?

I've been searching the internet and I'm having trouble finding any answers as to what's really going on behind the scenes with the foreach loop in C#.

I know this question doesn't really pertain to actually coding but its bothering me. I'm pretty new to OO programming and especially interfaces. I understand they are contracts and I understand how IEnumerable and IEnumerator work - or so I think.

I've been reading this article on MSDN: IEnumerable Interface

I understand how everything is set up. I'm a little unclear though in the Main loop how the foreach knows to itterate through all the values in _people. How does it know this? How does it keep track of Current by just calling return new PeopleEnum(_people);?

EDIT: I don't see how this is an exact duplicate. Yes its similar grounds and the same question is being asked but we are looking for different answers or the answer I wanted was not discussed in the previous question.

A foreach loop like foreach(int i in obj) {...} kinda equates to

... is "kinda" not the answer I'm looking for.

Community
  • 1
  • 1
Adam Beck
  • 1,271
  • 2
  • 14
  • 25
  • Have a look at the dll code using Ildasm.exe for a simple Foreach that should help. – Nikshep Sep 08 '11 at 15:28
  • I read that over and over. I know it tells you how to _use_ the `foreach` loop but it doesn't really explain how the compiller uses it. I was more interested in the behind the scenes. – Adam Beck Sep 08 '11 at 15:29
  • @Adam: In the accepted answer in the possible duplicate, it shows *roughly* the code it boils down to. Though, of course, Eric's answer below is much more detailed. – James Michael Hare Sep 08 '11 at 15:35
  • Well now when someone, like me, decides to Google "behind the scenes of the `foreach` loop, it will be in the top links and they will get a very specific, very detailed, answer. – Adam Beck Sep 08 '11 at 15:48
  • 2
    I don't believe it's an exact duplicate. The other question asks "which types of classes can use foreach loops" not a detailed explanation of how it works. – Meta-Knight Sep 08 '11 at 15:54

3 Answers3

24

I encourage you to read section 8.8.4 of the C# specification, which answers your question in detail. Quoting from it here for your convenience:


A foreach statement of the form

foreach (V v in x) embedded-statement

is then expanded to:

{
    E e = ((C)(x)).GetEnumerator();
    try 
    {
        V v;
        while (e.MoveNext()) 
        {
            v = (V)(T)e.Current;
            embedded-statement
        }
    }
    finally 
    {
         code to Dispose e if necessary
    }
}

The types E, C, V and T are the enumerator, collection, loop variable and collection element types deduced by the semantic analyzer; see the spec for details.

So there you go. A "foreach" is just a more convenient way of writing a "while" loop that calls MoveNext until MoveNext returns false.

A few subtle things:

  • This need not be the code that is generated; all that is required is that we generate code that produces the same result. For example, if you "foreach" over an array or a string, we just generate a "for" loop (or loops, in the case of multi-d arrays) that indexes the array or the chars of the string, rather than taking on the expense of allocating an enumerator.

  • If the enumerator is of value type then the disposal code might or might not choose to box the enumerator before disposing it. Don't rely on that one way or the other. (See http://blogs.msdn.com/b/ericlippert/archive/2011/03/14/to-box-or-not-to-box-that-is-the-question.aspx for a related issue.)

  • Similarly, if the casts automatically inserted above are determined to be identity conversions then the casts might be elided even if doing so would normally cause a value type to be copied.

  • Future versions of C# are likely to put the declaration of loop variable v inside the while loop body; this will prevent the common "modified closure" bug that is reported about once a day on Stack Overflow. [Update: This change has indeed been implemented in C# 5.]

Community
  • 1
  • 1
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • +1: so much for my extended explanation ... :) – IAbstract Sep 08 '11 at 15:29
  • @reggie: Please understand I don't want to know how to implement a `foreach` loop or what I need in order to use one. I want to know how the _compiler_ thinks. I want to know what it sees. I wasn't even aware these specification documents were out there. I will definitely be using them to answer several questions. Thank you. – Adam Beck Sep 08 '11 at 15:35
  • @Eric: what do you mean by saying: "code to Dispose e IF NECESSARY". When it necessary, actually? – Tigran Sep 08 '11 at 15:38
  • 1
    @Adam: I encourage you to first download the C# 4 specification, and then consider buying the printed version that Addison-Wesley publishes. My colleagues and I have extensively annotated it with the sorts of detailed explanations that it sounds like you are interested in. See http://blogs.msdn.com/b/ericlippert/archive/2010/11/15/the-annotated-fourth-edition-is-available.aspx. – Eric Lippert Sep 08 '11 at 15:46
  • @Tigran: It is *unnecessary* to do so if we know at compile time that the enumerator *does not* implement IDisposable. For example, if the enumerator is determined to be a struct or a sealed class that does not implement IDisposable. If the enumerator is *known* to implement IDisposable then we generate code to dispose it (if it is not null). If we are uncertain whether it implements IDisposable or not, then we generate code that tests the object at runtime and disposes if the runtime test succeeds. – Eric Lippert Sep 08 '11 at 15:49
  • 1
    @Tigran: And of course, if we determine that disposal is unnecessary then the entire try-finally is removed; we don't generate a try-protected region with an empty finally! – Eric Lippert Sep 08 '11 at 15:58
  • Is the `(T)` cast ever necessary? It's not clear from the spec that `T` can ever be different than the type returned from `e.Current`. – Gabe Sep 08 '11 at 16:03
  • 1
    @Gabe: Usually it is not different. This is mostly there for historical reasons. Consider: what if the collection is an array of T? In C# 1.0 the array implemented `IEnumerable`, not `IEnumerable`, so "Current" returns object, not T. – Eric Lippert Sep 08 '11 at 16:14
  • Just to add because it's interesting and rooted in C# 1.0: You don't even need IEnumerable. As seen in the expanded code, as long as you have GetEnumerator which has MoveNext and Current, foreach will iterate it (that's how C# 1.0 did strongly typed enumerators in the dark ages before generics) – Michael Stum Sep 08 '11 at 17:16
  • @EricLippert How is the iteration variable v readonly? I have posted this is a question: https://stackoverflow.com/questions/57940961/how-is-the-iteration-variable-readonly – David Klempfner Sep 15 '19 at 03:40
3

Consider it like this:

 var enumerator = <YourEnumerableHere>.GetEnumerator();

 while(enumerator.MoveNext())
    <YourForEachMethodBodyHere>

Ergo, when it calls GetEnumerator, you get back that result to the PeopleEnum. Every time the MoveNext method is called, the PeopleEnum's position increases by one, iterating over the list.

When the position has reached the end, the MoveNext call returns false, and the loop ends.

Tejs
  • 40,736
  • 10
  • 68
  • 86
0

It knows to iterate through the IEnumerable because that is what the language designers decided foreach should do. foreach is providing you a shorthand way of looping through an IEnumerable yourself.

The IEnumerator class is defining what it means to "get the next element". If you wanted to, you could make the IEnumerator return every other element, but most people just want to go through each element sequentially.

I am not sure if I have answered your question. If not, let me know and I will respond.

skaz
  • 21,962
  • 20
  • 69
  • 98