Обложка канала

.NET Разработчик

Опытный разработчик не так давно зашёл в .Net и поставил цель получить сертификат Microsoft. Свой ежедневный прогресс он описывает на канале .Net Разработчик. Заметки об изученном материале, советы по повышению производительности и поддержке мотивации, ин

.NET Разработчик

3 года назад
Открыть в
День 1491. #ЗаметкиНаПолях Какой Интерфейс Коллекции Использовать? Окончание Начало Продолжение 3. IEnumerable - тоже дырявая абстракция В предыдущем посте мы выяснили, что нужно использовать IReadOnlyList в качестве возвращаемого типа коллекции. Но почему так много известных библиотек вместо этого используют IEnumerable? Одна из причин — ленивая оценка. В .NET BCL большинство инструкций LINQ обрабатываются лениво при работе с IEnumerable:
var numbers = new int[] { 1, 2, 3 };
var r1 = numbers.Where(x => x > 1);
var r2 = r1.Select(x => x + 1);
int result = r2.Sum();

Здесь выполнение 2 и 3 строк откладывается до строки 4, где вызывается Sum. Если нужно поддерживать ленивую оценку, используйте IEnumerable, иначе IReadOnlyList. IEnumerable и ORM Обратите внимание, что ленивое вычисление также приводит к тому, что IEnumerable в некоторых сценариях является дырявой абстракцией:
// Student repository
public IEnumerable<Student> GetAll()
{
  using SchoolContext ctx = new();
  return ctx.Set<Student>();
}

// контроллер
public IEnumerable<StudentDto> Get()
{
  var students = _repo.GetAll();
  var dtos = students.Select(MapToDto);
  return dtod.ToList();
}

Код в контроллере выполняется только когда вызывается ToList. Если к этому моменту DbContext уже утилизирован, мы получим исключение. Это ещё один пример дырявой абстракции: EF Core требует от вас знать, открыто ли соединение с БД во время оценки IEnumerable. Хотя это не такая уж проблема, поскольку наличие постоянного DbContext является разумным предположением в большинстве веб-приложений. У вас почти всегда будет активный экземпляр DbContext на протяжении всей бизнес-операции. IEnumerable без ORM Реализации IEnumerable часто действуют как дырявые абстракции даже в сценариях без ORM. Например, BlockingCollection реализует IEnumerable таким образом, что он блокирует вызывающий поток в методе MoveNext() до тех пор, пока другой поток не добавит элемент в эту коллекцию. А при реализации «бесконечной» коллекции через yield return вызов ToList на такой коллекции приведёт к зависанию программы в бесконечном цикле. Технически это не дырявая абстракция, т.е. IEnumerable не обещает конечность коллекции, но на практике большинство программистов ожидают от IEnumerable определённого поведения. Можно сказать, что IEnumerable имеет неявный контракт, на который полагаются большинство программистов, и некоторые реализации IEnumerable нарушают его. IEnumerable в разработке библиотек Если вы разрабатываете библиотеку для публичного использования, лучше максимально ограничить её публичный API, чтобы дать себе возможность обновлять его. Это включает как количество публичных методов, так и типы, возвращаемые этими методами. Тогда предпочтительнее возвращать IEnumerable вместо IReadOnlyList. Это позволит изменить внутреннюю реализацию, например, List<T> на LinkedList<T>, не нарушая обратной совместимости. Итого - Используйте IEnumerable для аргументов как наиболее универсальный тип из возможных. - Используйте IReadOnlyList для возвращаемых значений как наиболее конкретный возможный тип. - IQueryable — дырявая абстракция, потому что она требует, чтобы вы знали, какие выражения LINQ может понять ORM. - IList и Array не подходят, потому что они изменяемы. - Имейте в виду, что некоторые реализации IEnumerable также являются дырявыми абстракциями. Источник: https://enterprisecraftsmanship.com/posts/which-collection-interface-to-use/