[.NET, Программирование] Принятого не воротай: Enumerable vs List

Автор Сообщение
news_bot ®

Стаж: 6 лет 9 месяцев
Сообщений: 27286

Создавать темы news_bot ® написал(а)
15-Сен-2020 10:30

Когда-то я работал в команде, где слегка недолюбливали LINQ, за то, что такой код якобы сложно отлаживать. У нас была договоренность: после каждой цепочки LINQ, разработчик создает локальную переменную, в которую записывает результат ToArray(). Независимо от того, потребуется ли массив далее по методу, или он работает только с IEnumerable. Перед return, результат также приводился к массиву, кажется, во всей кодовой базе не было методов, возвращающих или принимающих коллекцию, отличную от массива.Бородатое легаси! - подумаете вы и будете правы. Однако, несмотря то, что прошло много лет, с тех пор, как LINQ стал использоваться повсеместно, а IDE позволяют смотреть данные в отладке, некоторые разработчики все еще плохо представляют себе критерии выбора принимаемого и возвращаемого типа, если речь заходит о коллекциях.
Начнем с того, что есть коллекция? Поскольку BCL имеет одноименный тип данных, важно понимать, что коллекция - это тип данных, спроектированный для оперирования группой элементов, имеющих некую общую характеристику (тип данных). Таким образом, все, что можно перечислить является коллекцией.Предпочитайте абстракцииНезависимо от того, возвращаете вы или принимаете данные, удобнее работать с абстракциями. Вы контролируете доступ к объекту, предоставляя только необходимые члены. Вы также можете безболезненно поменять реализацию, не нарушая имеющийся код работы с параметром или возвращаемым значением. Возвращать конкретный тип имеет смысл, когда функционала стандартного интерфейса недостаточно, а выделять специфичный не имеет смысла.Lazy loadingВопрос - а прогрузились ли данные(например в IEnumerable) или нет, сам по себе не касается типа. Метод может возвращать как свою реализацию IList, так и стандартную, при этом с отложенной загрузкой данных. Информировать пользователя о том, используется ли lazy loading, посредством возвращаемого типа - плохая идея. Вы обременяете тип несвойственными ему обязанностями. Комментарий, либо специфичный постфикс 'Lazy' в названии метода, будут более удачным решением, если это не ясно из контекста.IRealonlyCollectionНесмотря на то, что по тем или иным причинам, у нас нет IArray, у нас есть IRealonlyCollection, добавляющий к перечислению свойство-размер. 
namespace System.Collections.Generic
{
  public interface IReadOnlyCollection : IEnumerable, IEnumerable
  {
    int Count { get; }
  }
}
Соответствующий класс-враппер был добавлен в версии фреймворка 4.5, для удобного создания read-only коллекций. К нему легко можно преобразовать как Array, так и List, поскольку оба они реализуют IList. С IEnumerable дела обстоят хуже… Здесь, чтобы получить IRealonlyCollection, вам так или иначе придётся сначала получить List. Таким образом, де-факто стандартом здесь будет являться List. Возвращать Array или IRealonlyCollection вместо List, смысл есть только когда вам важно подчеркнуть неизменяемость. Во всех остальных случаях IList предложит более широкую функциональность примерно за ту же стоимость.Возвращайте пустые коллекции вместо nullКажется, уже все про это знают, тем не менее, я то и дело, иногда, то тут, то там, получаю null. Это побуждает меня и коллег добавлять проверки на null, что сводит к минимуму комфортную работу с пустыми коллекциями. Задумайтесь - null это не 0 элементов. Возвращая 1 раз null, вы навсегда обрекаете пользователя штамповать проверки на null, там, где они избыточны. Например:
if(myEnumerable != null)
{
   foreach(var item in myEnumerable)
   {
   }
}
Хуже, но более лаконично:
foreach(var item in myEnumerable ?? Enumerable.Empty<T>())
{
}
IEnumerable/ICollection/IList Для начала, вкратце, что есть что: IEnumerable собственно, неизменяемая коллекция - перечисление, вам доступен только перечислительIReadOnlyCollection : IEnumerableнеизменяемая коллекция - перечисление, вам доступен перечислитель и размерICollection : IEnumerableколлекция c возможностью добавлять и удалять элемены, также доступен размер и признак изменяемости(IsReadOnly)IReadOnlyList : IReadOnlyCollectionнеизменяемая коллекция с порядком следования элементов, вам доступен индексаторIList : ICollectionколлекция с порядком следования элементов, c возможностью добавлять и удалять элемены по индексуПринимайте максимально обобщенный тип, нет смысла принимать больше данных, чем требуется методу для работы. Более того, передавать кучу неиспользуемых данных это антипаттерн. Принимая IEnumerable вы предоставляете возможность пользователю передать, в том числе и более конкретные типы - ICollection, IList… не пребегая к преобразованию.Возвращайте максимально конкретный тип, нет смысла прятать за обощенный интерфейс те данные, которыми вы уже располагаете, в результате работы метода. Если вы имеете массив то вы ничего не теряете, возвращая IRealonlyCollection. Возвращая IEnumerable вы скрываете знание о размере и в случае, если оно понадобится пользователю, прийдется изменять согнатуру метода, а если это невозможно - создавать дублирующий метод-перегрузку. Если результатом работы вашего метода является коллекция фиксированного размера и вы хотите избежать lazy loading, имеет смысл вернуть IList или ICollection, если вам важно указать пользователю на неизменяемость - их read-only аналоги.Web API и HTTPЕсли у вас одна часть приложения общается с другой частью по HTTP, например это разные слои сервисов или микросервисы, вы вероятно будете делать зарос из одного сервиса в другой через класс-клиент. И здесь, на секунду может показаться, что у вас есть выбор использовать что угодно, начиная с IEnumerable и заканчивая IList. На самом деле, по HTTP ваша коллекция уедет как JSON - серрилизованый массив, вся целиком. И приедет, если мы говорим о популярных дессерилизаторах(Newtonsoft.Json, System.Text.Json), не иначе как List. В данном случае нет никакого смысла отдавать\принимать что-то другое. Указывая IEnumerable в response контроллера вы только усложняете понимание кода.Я не касался темы производительности, поскольку, если ставить её во главу угла, критерии выбора будут сильно зависеть от конкретной ситуации, а это уже совсем другая история.Буду рад поправкам и дополнениям, рекомендую ознакомиться с Framework Design Guidelines for Collections.
===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_.net, #_programmirovanie (Программирование), #_c#, #_ienumerable, #_ilist, #_.net, #_programmirovanie (
Программирование
)
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 22-Ноя 18:26
Часовой пояс: UTC + 5