33

Possible Duplicate:
How does foreach work when looping through function results?

If I have functionality like the following - will ReturnParts() get called for each iteration in a foreach loop, or will it get called just the once?

private void PrintParts()
{
     foreach(string part in ReturnParts())
     {
         // Do Something or other. 
     }
}

private string[] ReturnParts()
{
     // Build up and return an array. 
}
Community
  • 1
  • 1
Robert W
  • 2,921
  • 7
  • 34
  • 42
  • 1
    Duplicate: http://stackoverflow.com/questions/1632810/how-does-foreach-work-when-looping-through-function-results – Adam Lear Mar 15 '10 at 13:51

4 Answers4

36

It will be called just once.

P.S. Calling it multiple times would make little sense. You would call it each time anew if you expected the result to be different each time. And how would you iterate over a continuously changing set?

  • Perhaps in a case where you're looking for exact characters in your strings and only care about the current iteration? – Hazel へいぜる Oct 24 '18 at 20:43
  • That's what I thought too (that methods are called only once) until last week. Here is the context : I have a `foreach` loop which uses a method as items source; the method is getting its results from an SQL query. **I was quite bewilded when I saw the query executing at each loop** (I had a break point on the method instruction within the `foreach` declaration and I turned on an SQL Server profiler in the mean time) ! Can anybody explain this behaviour ? – Ishikawa Jul 04 '19 at 15:44
22

You can determine this yourself by placing a breakpoint on the function "ReturnParts" If it is hits mutliple times for each iteration then yes it does.

Achilles
  • 11,165
  • 9
  • 62
  • 113
15

It will only be called once.

The foreach loop is equivalent to the following code:

IEnumerable<string> enumerator = (collection).GetEnumerator();
try {
   while (enumerator.MoveNext()) {
      string part = (string)enumerator.Current;

      // Do Something or other. 

   }
} finally {
   IDisposable disposable = enumerator as System.IDisposable;
   if (disposable != null) disposable.Dispose();
}
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
7

In wondering the differences between for, foreach, while, and goto a few weeks ago so I wrote up this test code. All of the methods will compile into the same IL (other then a variable name on the foreach version.) In debug mode a few NOP statements will be in different positions.

static void @for<T>(IEnumerable<T> input)
{
    T item;
    using (var e = input.GetEnumerator())
        for (; e.MoveNext(); )
        {
            item = e.Current;
            Console.WriteLine(item);
        }
}
static void @foreach<T>(IEnumerable<T> input)
{
    foreach (var item in input)
        Console.WriteLine(item);
}
static void @while<T>(IEnumerable<T> input)
{
    T item;
    using (var e = input.GetEnumerator())
        while (e.MoveNext())
        {
            item = e.Current;
            Console.WriteLine(item);
        }
}
static void @goto<T>(IEnumerable<T> input)
{
    T item;
    using (var e = input.GetEnumerator())
    {
        goto check;
    top:
        item = e.Current;
        Console.WriteLine(item);
    check:
        if (e.MoveNext())
            goto top;
    }
}
static void @gotoTry<T>(IEnumerable<T> input)
{
    T item;
    var e = input.GetEnumerator();
    try
    {
        goto check;
    top:
        item = e.Current;
        Console.WriteLine(item);
    check:
        if (e.MoveNext())
            goto top;
    }
    finally
    {
        if (e != null)
            e.Dispose();
    }
}

Per @Eric's comment...

I have expanded for, while, 'goto' and foreach with generic arrays. Now the for each statement looks to use the indexer for the array. Object arrays and strings are expanded in similar ways. Objects will remove a boxing that occurs before the method call to Console.WriteLine and Strings will replace T item and T[] copy... with char item and string copy... respectively. Note that the critical section is no longer need because the disposable enumerator is no longer used.

static void @for<T>(T[] input)
{
    T item;
    T[] copy = input;
    for (int i = 0; i < copy.Length; i++)
    {
        item = copy[i];
        Console.WriteLine(item);
    }
}
static void @foreach<T>(T[] input)
{
    foreach (var item in input)
        Console.WriteLine(item);
}
static void @while<T>(T[] input)
{
    T item;
    T[] copy = input;
    int i = 0;
    while (i < copy.Length)
    {
        item = copy[i];
        Console.WriteLine(item);
        i++;
    }
}
static void @goto<T>(T[] input)
{
    T item;
    T[] copy = input;
    int i = 0;
    goto check;
top:
    item = copy[i];
    Console.WriteLine(item);
    i++;
check:
    if (i < copy.Length)
        goto top;
}
Matthew Whited
  • 22,160
  • 4
  • 52
  • 69
  • @gotoTry is the most expanded version I can think of without showing the compiled IL. – Matthew Whited Mar 15 '10 at 14:06
  • 2
    Nicely done. Of course, this is the expansion only for things which implement IEnumerable. foreach generates different code for arrays and strings. – Eric Lippert Mar 15 '10 at 14:22
  • @Eric: thanks for the feed back. I was going for the most generic version, but I may extend this futher later. (I was mainly interested in the goto version seein that most still consider it "evil".) – Matthew Whited Mar 15 '10 at 14:44