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