tags:

views:

223

answers:

4

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. 
}
+8  A: 

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?

Developer Art
+5  A: 

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
+7  A: 

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
+4  A: 

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
@gotoTry is the most expanded version I can think of without showing the compiled IL.
Matthew Whited
Nicely done. Of course, this is the expansion only for things which implement IEnumerable<T>. foreach generates different code for arrays and strings.
Eric Lippert
@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