Обзор вопросов по языка програмиированию C# и технологии NET
Реализуется через интерфейсы:
IEnumerable<T>
— предоставляет GetEnumerator()
IEnumerator<T>
— предоставляет Current
и MoveNext()
IEnumerable<T>
public interface IEnumerable<out T>
{
IEnumerator<T> GetEnumerator();
}
IEnumerator<T>
public interface IEnumerator<out T> : IDisposable
{
T Current { get; }
bool MoveNext();
void Reset(); // редко используется
}
yield return
yield return
позволяет создавать итератор без ручной реализации IEnumerator.IEnumerable<int> GetNumbers()
{
for (int i = 0; i < 5; i++)
yield return i;
}
Особенности:
IEnumerator<T>
.var numbers = GetNumbers(); // метод ещё не выполняется
foreach(var n in numbers) // тут начинается генерация
Console.WriteLine(n);
Преимущества:
Минусы:
yield break
IEnumerable<int> NumbersUpTo(int max)
{
for (int i = 0; i < max; i++)
{
if (i == 3)
yield break; // завершение итератора
yield return i;
}
}
Deferred vs Immediate
yield return
→ deferred.ToList()
→ immediate.Многоэтапные итераторы
yield return
в условных блоках, каждый поддерживает своё состояние.Изменение коллекции во время итерации
InvalidOperationException
(для большинства коллекций, кроме Concurrent).Lazy evaluation
IEnumerator.Dispose()
foreach
использует try-finally).Структурные итераторы
struct
для уменьшения накладных расходов на GC.Несколько foreach на одном IEnumerable
IEnumerable<int> FilterAndSquare(IEnumerable<int> numbers)
{
foreach (var n in numbers)
{
if (n % 2 == 0)
yield return n * n; // только чётные возводим в квадрат
}
}
FilterAndSquare(list)
не выполнит ничего, пока не начнётся foreach.Select
, Where
) возвращают **IEnumerablevar evens = numbers.Where(n => n % 2 == 0); // ещё не выполняется
foreach(var n in evens)
Console.WriteLine(n); // здесь выполнение
Вопросы:
ToList()
, Count()
, Sum()
)yield return
vs обычный List
IEnumerator
vs IEnumerable
IEnumerable
предоставляет enumerator, который делает фактический перебор.Можно ли использовать yield return
в асинхронных методах?
IAsyncEnumerable<T>
и await foreach
.Как устроен внутренний state machine компилятора для yield return?
Почему foreach
вызывает Dispose?
Характеристика | IEnumerable |
IEnumerator |
yield return |
---|---|---|---|
Кто предоставляет | Коллекция | Перебор | Компилятор создаёт enumerator |
Deferred execution | Да | Нет | Да |
Dispose | Нет | Да | Да (через enumerator) |
Много переборов | Да | Один | Да, каждый GetEnumerator новый |
Сложность реализации | Нужно вручную | Нужно вручную | Очень просто, компилятор генерирует класс |
✨Dvurechensky✨