Обзор вопросов по языка програмиированию C# и технологии NET
yield return
yield break
yield return
return
yield return
yield return
позволяет поэтапно возвращать элементы коллекции, не создавая сразу весь массив/список.yield return
, называется итератором.IEnumerable<T>
/IEnumerator<T>
.Пример:
IEnumerable<int> GetNumbers()
{
for (int i = 0; i < 5; i++)
{
yield return i;
}
}
Использование:
foreach (var n in GetNumbers())
{
Console.WriteLine(n); // числа от 0 до 4
}
Компилятор превращает метод с yield return
в состояниевую машину (state machine):
IEnumerable<T>
и IEnumerator<T>
.state
) сохраняются между вызовами MoveNext()
.Благодаря этому:
yield
IEnumerable<int> GetNumbers()
{
var list = new List<int>();
for (int i = 0; i < 5; i++)
list.Add(i);
return list;
}
yield
IEnumerable<int> GetNumbers()
{
for (int i = 0; i < 5; i++)
yield return i;
}
yield break
IEnumerable<int> GetNumbers()
{
for (int i = 0; i < 10; i++)
{
if (i == 5) yield break;
yield return i;
}
}
yield return
Ленивая генерация данных
Простота кода
IEnumerator<T>
.Подходит для бесконечных последовательностей
IEnumerable<int> Infinite()
{
int i = 0;
while(true)
yield return i++;
}
var evens = GetNumbers().Where(n => n % 2 == 0);
yield return
создаёт state machine → небольшой накладной класс + хранение локальных переменных.list[i]
.Многопоточность:
yield return
не потокобезопасен.foreach
.Dispose
IDisposable
.foreach
досрочно, вызывается Dispose
(например, закрывает файл, поток и т.п.).Пример с using
:
IEnumerable<string> ReadLines(string path)
{
using var reader = new StreamReader(path);
string line;
while ((line = reader.ReadLine()) != null)
yield return line;
}
Dispose
вызовется при завершении итерации или при досрочном выходе.return
Характеристика | return |
yield return |
---|---|---|
Возвращает один объект или коллекцию | Один раз | Множество значений поэтапно |
Ленивая генерация | Нет | Да |
Память | Всё сразу | Только текущий элемент + state |
Итератор | Нет | Да (IEnumerable /IEnumerator ) |
Прерывание | return завершает метод |
yield break завершает итерацию |
Состояние локальных переменных | Не сохраняется между вызовами | Сохраняется в state machine |
Что происходит в памяти при yield return
?
state
), локальными переменными и текущим элементом (Current
).Можно ли использовать yield return
в async методе?
async
метод не может содержать yield return
. Для асинхронной ленивой генерации используют IAsyncEnumerable<T>
и await foreach
.Что если метод с yield return
выбрасывает исключение?
MoveNext()
).Можно ли использовать yield return
с ref
типами?
ref
через yield return
напрямую — только копию значения.Что если коллекция изменяется после начала итерации через yield return
?
foreach
, классический InvalidOperationException
будет, как и при обычном foreach
.yield return
и многократная итерация
GetEnumerator()
создаёт новый экземпляр state machine → можно безопасно повторять итерацию.Можно ли комбинировать yield return
и обычные коллекции?
foreach(var item in list) yield return item; // да, часто используют для фильтров и проекций
IEnumerable<int> Fibonacci()
{
int a = 0, b = 1;
while(true)
{
yield return a;
int tmp = a;
a = b;
b = tmp + b;
}
}
IEnumerable<string> ReadLines(string path)
{
using var reader = new StreamReader(path);
string line;
while ((line = reader.ReadLine()) != null)
yield return line;
}
yield return
и фильтрацииIEnumerable<int> EvenNumbers(IEnumerable<int> numbers)
{
foreach(var n in numbers)
if(n % 2 == 0) yield return n;
}
Что такое yield return
?
Позволяет возвращать элементы коллекции поэтапно, лениво.
Чем отличается от обычного return
?
return
— разово возвращает значение; yield return
— поэтапно через enumerator.
Когда выполняется код метода с yield return
?
Только при фактической итерации (при MoveNext()
enumerator’а).
Что такое state machine в контексте yield return
?
Внутренний класс, который хранит состояние метода, локальные переменные и текущий элемент между итерациями.
Можно ли использовать yield return
в async методе?
Нет. Для асинхронной итерации используется IAsyncEnumerable<T>
.
Что делает yield break
?
Прекращает итерацию досрочно.
Как yield return
влияет на память?
Экономит память, т.к. не создаётся вся коллекция сразу; хранится только текущий элемент + state.
Что произойдет, если коллекция изменится во время итерации с yield return
?
Как и с foreach
, может быть выброшено InvalidOperationException
.
✨Dvurechensky✨