Обзор вопросов по языка програмиированию C# и технологии NET
yield returnyield breakyield returnreturnyield returnyield 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().Благодаря этому:
yieldIEnumerable<int> GetNumbers()
{
var list = new List<int>();
for (int i = 0; i < 5; i++)
list.Add(i);
return list;
}
yieldIEnumerable<int> GetNumbers()
{
for (int i = 0; i < 5; i++)
yield return i;
}
yield breakIEnumerable<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✨