С# - Вызывается ли функция для каждой итерации цикла foreach?

Возможный дубликат:
Как работает foreach при выполнении циклов через функцию?

Если у меня есть функциональность, такая как следующее: будет ли возвращать ReturnParts() для каждой итерации в цикле foreach или будет вызван только один раз?

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

private string[] ReturnParts()
{
     // Build up and return an array. 
}

Ответ 1

Он будет вызываться только один раз.

P.S. Называть его несколько раз было бы мало смысла. Вы будете называть его каждый раз заново, если вы ожидаете, что результат будет разным каждый раз. И как бы вы перебирали непрерывно меняющийся набор?

Ответ 2

Вы можете сами определить это, поставив точку останова на функцию "Возвратные части". Если она имеет несколько раз для каждой итерации, тогда да.

Ответ 3

Он будет вызываться только один раз.

Цикл foreach эквивалентен следующему коду:

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();
}

Ответ 4

Задумываясь о различиях между foreach, while и goto несколько недель назад, я написал этот тестовый код. Все методы будут скомпилированы в один и тот же IL (другой - имя переменной в версии foreach). В режиме отладки несколько операторов NOP будут находиться в разных положениях.

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();
    }
}

За комментарий @Eric...

Я расширил for, while, 'goto' и foreach с помощью общих массивов. Теперь в инструкции for each используется указатель для массива. Массивы объектов и строки расширяются аналогичным образом. Объекты удалят бокс, который возникает до вызова метода в Console.WriteLine и Strings заменит T item и T[] copy... на char item и string copy... соответственно. Обратите внимание, что критический раздел больше не нужен, потому что одноразовый счетчик больше не используется.

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;
}