День 1490. #ЗаметкиНаПолях
Какой Интерфейс Коллекции Использовать? Продолжение
Начало
2. IList и Array
Заметьте, что здесь говорится об интерфейсе IList, а не конкретном типе List. Они почти идентичны с точки зрения функциональности, и поэтому, хотя технически List более конкретен, чем IList, на практике не имеет большого значения, какой из них вы используете.
// вариант 1
public IList<Student> GetAll()
// вариант 2
public Student[] GetAll()
При принятии решения о том, какой тип использовать в качестве возвращаемого значения, мы не только должны быть осторожны с утечкой абстракций, но также должны учитывать, правильно ли этот тип представляет то, что клиент может и не может делать с возвращаемыми данными.
Например, клиентский код может изменить список студентов после того, как получит их из репозитория. Вы можете делать это по двум причинам:
- Вы изменяете коллекцию, чтобы добавить или удалить студентов из БД.
- Вы повторно используете коллекцию для других целей. Например, нужно отфильтровать студентов по некоторым критериям, и вместо создания новой коллекции вы удаляете элементы из существующей коллекции.
Обе эти причины неправильные.
Возврат изменяемой коллекции создаёт впечатление, что студентов, которых она представляет, можно изменить, просто добавив или удалив элемент из коллекции, что не так. В тот момент, когда информация о студентах покидает базу данных, эта информация отсоединяется от базы данных, и её изменение ничего не даёт.
Замечание: да, ORM (NHibernate или EF Core) позволяет эмулировать связь объектов с данными в базе, но это не наш случай, когда мы возвращаем коллекцию из метода.
Поэтому мы не должны использовать изменяемую коллекцию, поскольку она не представляет должным образом то, что клиент может и не может делать с данными в БД.
Мы также не должны модифицировать коллекцию, чтобы повторно использовать её для каких-то других целей. Если у вас нет серьёзных ограничений производительности (что почти никогда не бывает), нужно просто создать новую коллекцию с отфильтрованными данными. Ненужная модификация данных часто приводит к целому ряду сложно поддающихся отладке проблем, которых можно избежать, просто сделав эти данные неизменяемыми. Поэтому мы должны использовать только неизменяемые типы коллекций как для аргументов, так и для возвращаемых значений.
Таким образом, у нас остались:
- IEnumerable для аргументов как наиболее общий тип из возможных,
- IReadOnlyList для возвращаемых значений как наиболее конкретный возможный тип.
Мы используем IReadOnlyList вместо IEnumerable, потому что IReadOnlyList обеспечивает больше функциональности: свойство Count, а также доступ к коллекции через индекс.
Замечание: IReadOnlyList не обязательно является неизменяемым «под капотом». Конкретный тип List также реализует IReadOnlyList, и теоретически вы можете привести IReadOnlyList обратно к List и изменить его. Но это привязывает ваш код к деталям реализации метода, возвращающего IReadOnlyList, и делает этот код ненадёжным. Клиентский код не должен полагаться ни на какую информацию, кроме предоставленной сигнатурой метода, и не должен делать никаких предположений об объекте, кроме того, что говорит его тип. Другими словами, никакого даункастинга.
Если вам необходимо изменять возвращаемую коллекцию, просто верните IList вместо IReadOnlyList или создайте новую коллекцию на основе имеющегося IReadOnlyList.
Окончание следует…
Источник: https://enterprisecraftsmanship.com/posts/which-collection-interface-to-use/