Как работает foreach при прохождении через функцию?

Предположим, что у меня есть следующий код:

foreach(string str in someObj.GetMyStrings())
{
    // do some stuff
}

Будет ли вызываться someObj.GetMyStrings() на каждой итерации цикла? Было бы лучше сделать следующее:

List<string> myStrings = someObj.GetMyStrings();
foreach(string str in myStrings)
{
    // do some stuff
}

?

Ответ 1

Функция, вызываемая только один раз, возвращает IEnumerator<T>; после этого метод MoveNext() и свойство Current используются для повторения результатов:

foreach (Foo f in GetFoos())
{
    // Do stuff
}

несколько эквивалентен:

using (IEnumerator<Foo> iterator = GetFoos().GetEnumerator())
{
    while (iterator.MoveNext())
    {
        Foo f = iterator.Current;
        // Do stuff
    }
}

Обратите внимание, что итератор расположен в конце - это особенно важно для размещения ресурсов из блоков итератора, например:

public IEnumerable<string> GetLines(string file)
{
    using (TextReader reader = File.OpenText(file))
    {
        string line;
        while ((line = reader.ReadLine()) != null)
        {
            yield return line;
        }
    }
}

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

Ответ 2

Нет. функция будет вызвана один раз, чтобы получить IEnumerable.., а затем повторный вызов MoveNext и Current.

Ответ 3

GetMyStrings() перенастраивает объект типа IEnumerable. Среда выполнения знает, как с этим бороться. Он вызывает IEnumerable.GetEnumerator(), а затем этот объект перечислителя вызывает MoveNext() и Current.

Ответ 4

Просто, чтобы закруглить вопрос, если вы попадаете в "все для одного и того же";   for (int x = 0; x < ProvideCeiling(); x ++) вызывает функцию каждый раз.