Коллекция вопросов ❓ на собеседовании в C# 🍧

Обзор вопросов по языка програмиированию C# и технологии NET


Project maintained by Dvurechensky-Docs Hosted on GitHub Pages — Theme by mattgraham

📤 Как работает `yield return` и для чего он используется?

Typing SVG

Static Badge

✨ Оглавление

⬆ Вернуться к главной


1️⃣ Что такое yield return

Пример:

IEnumerable<int> GetNumbers()
{
    for (int i = 0; i < 5; i++)
    {
        yield return i;
    }
}

Использование:

foreach (var n in GetNumbers())
{
    Console.WriteLine(n); // числа от 0 до 4
}

2️⃣ Что делает компилятор


3️⃣ Отличие от обычного возврата коллекции

Без 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;
}

4️⃣ yield break

IEnumerable<int> GetNumbers()
{
    for (int i = 0; i < 10; i++)
    {
        if (i == 5) yield break;
        yield return i;
    }
}

5️⃣ Преимущества yield return

  1. Ленивая генерация данных

    • Не нужно создавать полный список.
    • Экономия памяти и времени.
  2. Простота кода

    • Не нужно вручную реализовывать IEnumerator<T>.
  3. Подходит для бесконечных последовательностей

IEnumerable<int> Infinite()
{
    int i = 0;
    while(true)
        yield return i++;
}
  1. Комбинируется с LINQ
var evens = GetNumbers().Where(n => n % 2 == 0);

6️⃣ Производительность и подводные камни

  1. Каждый yield return создаёт state machine → небольшой накладной класс + хранение локальных переменных.
  2. Нет random access — нельзя сделать list[i].
  3. Многопоточность:

    • yield return не потокобезопасен.
    • Поведение при модификации коллекции во время итерации такое же, как и у foreach.
  4. 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;
}

7️⃣ Отличия от обычного return

Характеристика return yield return
Возвращает один объект или коллекцию Один раз Множество значений поэтапно
Ленивая генерация Нет Да
Память Всё сразу Только текущий элемент + state
Итератор Нет Да (IEnumerable/IEnumerator)
Прерывание return завершает метод yield break завершает итерацию
Состояние локальных переменных Не сохраняется между вызовами Сохраняется в state machine

8️⃣ Каверзные моменты, которые могут спрашивать на собесе

  1. Что происходит в памяти при yield return?

    • Создаётся вложенный класс-итератор с полями состояния (state), локальными переменными и текущим элементом (Current).
  2. Можно ли использовать yield return в async методе?

    • Нет, async метод не может содержать yield return. Для асинхронной ленивой генерации используют IAsyncEnumerable<T> и await foreach.
  3. Что если метод с yield return выбрасывает исключение?

    • Исключение не выбрасывается при вызове метода, а при фактической итерации (MoveNext()).
  4. Можно ли использовать yield return с ref типами?

    • Нельзя возвращать ref через yield return напрямую — только копию значения.
  5. Что если коллекция изменяется после начала итерации через yield return?

    • Если коллекция используется внутри foreach, классический InvalidOperationException будет, как и при обычном foreach.
  6. yield return и многократная итерация

    • Каждый вызов GetEnumerator() создаёт новый экземпляр state machine → можно безопасно повторять итерацию.
  7. Можно ли комбинировать yield return и обычные коллекции?

foreach(var item in list) yield return item; // да, часто используют для фильтров и проекций

9️⃣ Примеры использования

Генерация чисел Фибоначчи

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;
}

🔟 Вопросы на собеседовании

  1. Что такое yield return? Позволяет возвращать элементы коллекции поэтапно, лениво.

  2. Чем отличается от обычного return? return — разово возвращает значение; yield return — поэтапно через enumerator.

  3. Когда выполняется код метода с yield return? Только при фактической итерации (при MoveNext() enumerator’а).

  4. Что такое state machine в контексте yield return? Внутренний класс, который хранит состояние метода, локальные переменные и текущий элемент между итерациями.

  5. Можно ли использовать yield return в async методе? Нет. Для асинхронной итерации используется IAsyncEnumerable<T>.

  6. Что делает yield break? Прекращает итерацию досрочно.

  7. Как yield return влияет на память? Экономит память, т.к. не создаётся вся коллекция сразу; хранится только текущий элемент + state.

  8. Что произойдет, если коллекция изменится во время итерации с yield return? Как и с foreach, может быть выброшено InvalidOperationException.


⬆ Вернуться к главной

✨Dvurechensky✨