Вызывается ли функция для каждой итерации цикла 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;
}