tgoop.com/csharp_gepard/86
Last Update:
Свой Enumerator #память
Могие почему-то боятся делать перечислители (Enumerator) для своих коллекций, выставляя наружу внутренние массивы List
или Dictionary
. Чуть более смелые разработчики, желая сохранить инкапсуляцию, выставляют из сущностей IEnumerable
или даже более правильный IReadOnlyCollection
, делая свои коллекции приватными, но доступными через свойство. А вот если нам нужно что-то сделать перед передачей элемента коллекции из сущности, то добро пожаловать в LINQ: коллеги просто возвращают IEnumerable
где начинают городить в возвращаемом свойстве что-то вроде _collection.Select(id => new Actor(conext, id))
.
Давайте не будем так делать. Перечислители писать легко, благодаря duck-typing'у, который только и требует от нас написать одно свойство (Current) и два метода (MoveNext и GetEnumerator). Для примера, вот так будет выглдяеть enumerator для ref struct
(обратите внимание, что я использую современный синтакс C#).
public ref struct Enumerator(int[] collection, MyContext context) {
public readonly Result Current => new(context, collection[_index]);
private int _index = -1;
public bool MoveNext() => ++_index < collection.Length;
}
В нашей собственной коллекции мы можем также, как и выше, воспользоваться duck-typing'ом, банально реализовав метод
GetEnumerator
:
pubic class MyCollection {
private int[] _collection;
private MyContext _context;
...
public Enumerator GetEnumerator() => new(_collection, _context);
...
}
Таким образом мы решаем сразу несколько проблем:
1. Мы не выставляем наружу внутреннюю коллекцию.
2. Мы возвращаем нормальный enumerator в виде
struct
, который ничего не аллоцирует.3. Мы не производим замыкание переменных (clousure) при перечислении (см. свойство
Current
, где можно нагородить любую логику).Код примера в комментариях, если я объяснил слишком туманно.
BY C# Heppard
Share with your friend now:
tgoop.com/csharp_gepard/86