38

Suppose I have the following code:

foreach(string str in someObj.GetMyStrings())
{
    // do some stuff
}

Will someObj.GetMyStrings() be called on every iteration of the loop? Would it be better to do the following instead:

List<string> myStrings = someObj.GetMyStrings();
foreach(string str in myStrings)
{
    // do some stuff
}

?

Adam Lear
  • 38,111
  • 12
  • 81
  • 101
  • 23
    Try it and see - Put a breakpoint in `GetMyStrings()` and see how many times the debugger stops there! – AakashM Oct 27 '09 at 18:38

4 Answers4

54

The function's only called once, to return an IEnumerator<T>; after that, the MoveNext() method and the Current property are used to iterate through the results:

foreach (Foo f in GetFoos())
{
    // Do stuff
}

is somewhat equivalent to:

using (IEnumerator<Foo> iterator = GetFoos().GetEnumerator())
{
    while (iterator.MoveNext())
    {
        Foo f = iterator.Current;
        // Do stuff
    }
}

Note that the iterator is disposed at the end - this is particularly important for disposing resources from iterator blocks, e.g.:

public IEnumerable<string> GetLines(string file)
{
    using (TextReader reader = File.OpenText(file))
    {
        string line;
        while ((line = reader.ReadLine()) != null)
        {
            yield return line;
        }
    }
}

In the above code, you really want the file to be closed when you finish iterating, and the compiler implements IDisposable cunningly to make that work.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Correct me if I am wrong, But i think C# compiler does not allow you to edit the IEnumerable while the foreach loop is running. – Shaswat Rungta Aug 26 '16 at 06:37
  • @ShaswatRungta: I don't know what you mean by "edit the IEnumerable" or how this is relevant to the question. – Jon Skeet Aug 26 '16 at 06:43
  • If you try to Edit the IEnumerable once the foreach has started executing, the application throws an exception during run time Try to run this code http://pastebin.com/MztPyiuA . – Shaswat Rungta Aug 26 '16 at 07:22
  • @ShaswatRungta: Compilers can't throw exceptions - they're not involved at execution time. And you still haven't said what you mean by "edit the IEnumerable". I suspect you're getting confused with the safeguards that *some collection types* (e.g. `List`) but in place. If you modify a `List` while you're iterating over it, yes, you'll get an exception. That's a decision made by the `List` implementation - I don't see how it's relevant to this question. – Jon Skeet Aug 26 '16 at 07:23
  • @JonSkeet I thought this was because the List was processed as an IEnumerable for foreach loop and the loop was not allowing it to be edited while iterating over it. [Used the word compiler by mistake. I meant during run time] – Shaswat Rungta Aug 26 '16 at 07:28
  • @ShaswatRungta: "The loop" can't detect this at all - there's nothing on `IEnumerable` or `IEnumerator` to monitor changes. It's a per-collection decision. The concurrent collections *do* allow modification while iterating, for example. – Jon Skeet Aug 26 '16 at 07:29
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/121897/discussion-between-shaswat-rungta-and-jon-skeet). – Shaswat Rungta Aug 26 '16 at 07:30
19

No.. the function will get called once to get the IEnumerable.. and then there will be repeated call to MoveNext and Current.

Nestor
  • 13,706
  • 11
  • 78
  • 119
4

GetMyStrings() retuns an object of type IEnumerable. The runtime knows how to deal with that. It calls IEnumerable.GetEnumerator() and then on that enumerator object calls MoveNext() and Current.

Matthew
  • 2,210
  • 1
  • 19
  • 30
2

Just to round the question off in case you fall into a "all for's are the same";

for(int x=0;x < ProvideCeiling(); x++)

does call the function each time.

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Paul M
  • 21
  • 1