Я не понимаю, какой тип коллекции я должен возвращать из моих общедоступных методов и свойств API.
Я имею в виду коллекции IList
, ICollection
и Collection
.
Всегда ли предпочтительнее возвращать один из этих типов по сравнению с другими или это зависит от конкретной ситуации?
c#
.net
generics
collections
Рокки Сингх
источник
источник
Ответы:
Как правило, вы должны возвращать тип, который является как можно более общим, то есть такой, который знает достаточно возвращаемых данных, которые необходимо использовать потребителю. Таким образом, у вас будет большая свобода изменить реализацию API, не нарушая код, который его использует.
Считайте также
IEnumerable<T>
интерфейс возвращаемым типом. Если результат будет только повторяться, потребителю не нужно больше этого.источник
Count
метода проверяет фактический тип коллекции, поэтому он будет использовать свойстваLength
илиCount
для некоторых известных типов, таких как массивы, иICollection
даже когда вы предоставляете его какIEnumerable
.Thing<T>
реализует,IList<T>
но не неуниверсальныйICollection
, тогда вызовIEnumerable<Cat>.Count()
aThing<Cat>
будет быстрым, но вызовIEnumerable<Animal>.Count()
будет медленным (поскольку метод расширения будет искать, а не находить, реализациюICollection<Cat>
). Однако, если класс реализует неуниверсальныйICollection
,IEnumerable<Animal>.Count
он найдет и использует это.ICollection<T>
представляет собой интерфейс , который предоставляет коллекцию семантики , такие какAdd()
,Remove()
, иCount
.Collection<T>
это конкретная реализацияICollection<T>
интерфейса.IList<T>
по сути, имеетICollection<T>
доступ в произвольном порядке.В этом случае вы должны решить, требуется ли для ваших результатов семантика списка, такая как индексирование на основе порядка (затем используйте
IList<T>
), или вам просто нужно вернуть неупорядоченный «мешок» результатов (затем использоватьICollection<T>
).источник
Collection<T>
орудияIList<T>
и не толькоICollection<T>
.IEnumerable<T>
заказаны. Что отличаетIList<T>
от, такICollection<T>
это то, что он предлагает участникам работать с индексами. Например,list[5]
работает, ноcollection[5]
не компилируется.IList<T>
. Есть множество заказанных коллекций, которых нет.IList<T>
о быстром индексированном доступе.IEnumerable<T>
заказаны? БериHashSet<T>
- реализуетIEnumerable<T>
но явно не заказано. Т.е. вы не можете повлиять на порядок элементов, и этот порядок может измениться в любое время, если внутренняя хеш-таблица будет реорганизована.Основное различие между
IList<T>
иICollection<T>
заключается в том, чтоIList<T>
вы можете получать доступ к элементам через индекс.IList<T>
описывает типы, подобные массивам.ICollection<T>
Доступ к элементам в объекте можно получить только через перечисление. Оба позволяют вставлять и удалять элементы.Если вам нужно только перечислить коллекцию,
IEnumerable<T>
это предпочтительнее. Он имеет два преимущества перед остальными:Он запрещает вносить изменения в коллекцию (но не в элементы, если они ссылочного типа).
Он допускает максимально возможное разнообразие источников, включая перечисления, которые генерируются алгоритмически и вообще не являются коллекциями.
Collection<T>
- это базовый класс, который в основном полезен разработчикам коллекций. Если вы предоставите его в интерфейсах (API), многие полезные коллекции, не являющиеся производными от него, будут исключены.Одним из недостатков
IList<T>
является то, что массивы реализуют это, но не позволяют добавлять или удалять элементы (т.е. вы не можете изменять длину массива). Если вы вызываетеIList<T>.Add(item)
массив, будет выброшено исключение . Ситуация несколько смягчена, посколькуIList<T>
есть логическое свойство,IsReadOnly
которое вы можете проверить, прежде чем пытаться это сделать. Но на мой взгляд, это по-прежнему недостаток дизайна библиотеки . Поэтому используюList<T>
напрямую, когда требуется возможность добавлять или удалять элементы.источник
Collection
,ReadOnlyCollection
илиKeyedCollection
. И этоList
не должно быть возвращено.IList<T>
, невозможно гарантировать, что метод не будет вызываться с массивом в качестве параметра.IList<T>
является базовым интерфейсом для всех общих списков. Поскольку это упорядоченная коллекция, реализация может выбрать порядок, начиная от отсортированного до порядка вставки. Кроме того,Ilist
есть свойство Item, которое позволяет методам читать и редактировать записи в списке на основе их индекса. Это позволяет вставлять, удалять значение в / из списка по индексу позиции.Кроме того
IList<T> : ICollection<T>
,ICollection<T>
здесь доступны для реализации все методы из .ICollection<T>
является базовым интерфейсом для всех общих коллекций. Он определяет размер, счетчики и методы синхронизации. Вы можете добавить или удалить элемент в коллекцию, но вы не можете выбрать, в какой позиции это происходит из-за отсутствия свойства index.Collection<T>
обеспечивает реализациюIList<T>
,IList
иIReadOnlyList<T>
.Если вы используете более узкий тип интерфейса, например
ICollection<T>
вместоIList<T>
, вы защитите свой код от критических изменений. Если вы используете более широкий тип интерфейса, напримерIList<T>
, вы больше рискуете нарушить изменения кода.Цитата из источника ,
источник
Возврат типа интерфейса является более общим, поэтому (не имея дополнительной информации о вашем конкретном варианте использования) я бы склонился к этому. Если вы хотите выставить поддержку индексирования, выберите
IList<T>
, иначеICollection<T>
будет достаточно. Наконец, если вы хотите указать, что возвращаемые типы доступны только для чтения, выберитеIEnumerable<T>
.И, если вы не читали его раньше, Брэд Абрамс и Кшиштоф Квалина написали отличную книгу под названием «Рекомендации по разработке фреймворка: соглашения, идиомы и шаблоны для многоразовых библиотек .NET» (вы можете скачать дайджест отсюда ).
источник
IEnumerable<T>
только чтение? Конечно, но при перенастройке в контексте репозитория даже после выполнения ToList не является потокобезопасным. Проблема в том, что когда исходный источник данных получает GC, IEnumarble.ToList () не работает в многопоточном контексте. Он работает по линейномуasync
принципу, потому что GC вызывается после того, как вы выполняете работу, но использование дней в 4.5+ IEnumarbale становится головной болью.Из этого вопроса вытекает несколько тем:
Вы можете выделить, что это объектно-ориентированный API
интерфейсы против классов
Если у вас нет большого опыта работы с интерфейсами, я рекомендую придерживаться классов. Я часто вижу, как разработчики перескакивают на интерфейсы, даже если это не обязательно.
И, наконец, закончите делать плохой дизайн интерфейса вместо хорошего дизайна класса, который, кстати, в конечном итоге можно перенести на хороший дизайн интерфейса ...
Вы увидите много интерфейсов в API, но не торопитесь, если он вам не нужен.
Со временем вы узнаете, как применять интерфейсы к вашему коду.
какой конкретный класс из нескольких одинаковых классов, коллекции, списка, массива?
В C # (dotnet) есть несколько классов, которые можно менять местами. Как уже упоминалось, если вам нужно что-то из более конкретного класса, например «CanBeSortedClass», сделайте это явным образом в своем API.
Действительно ли вашему пользователю API нужно знать, что ваш класс можно отсортировать, или применить какой-то формат к элементам? Затем используйте CanBeSortedClass или ElementsCanBePaintedClass, в противном случае используйте GenericBrandClass.
В противном случае используйте более общий класс.
Общие классы коллекций по сравнению с коллекциями подэлементов ("обобщенных")
Вы обнаружите, что есть классы, содержащие другие элементы, и вы можете указать, что все элементы должны быть определенного типа.
Общие коллекции - это те классы, которые вы можете использовать одну и ту же коллекцию для нескольких программных приложений без необходимости создавать новую коллекцию для каждого нового типа подэлементов, например: Коллекция .
Потребуется ли вашему пользователю API очень специфический тип, одинаковый для всех элементов?
Используйте что-нибудь вроде
List<WashingtonApple>
.Потребуется ли вашему пользователю API несколько связанных типов?
Expose
List<Fruit>
для API, и использоватьList<Orange>
List<Banana>
,List<Strawberry>
внутри, гдеOrange
,Banana
иStrawberry
являются потомкамиFruit
.Потребуется ли вашему пользователю API общая коллекция типов?
Используйте
List
, где находятся все предметыobject
.Приветствия.
источник