Вызывается ли функция для каждой итерации цикла for each?

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

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

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

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

4 ответа

Решение

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

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

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

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

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

Задумавшись о различиях между for, 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();
    }
}

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

Я расширил for, while, "Перейти" и 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;
}
Другие вопросы по тегам