Чтобы ответить на вопрос «почему» на вопрос «почему бы и нет» List<T>
, причинами являются перспектива на будущее и простота API.
Будущие корректуры
List<T>
не предназначен для того, чтобы быть легко расширяемым за счет его подкласса; это разработано, чтобы быть быстрым для внутренних реализаций. Вы заметите, что методы на нем не являются виртуальными и поэтому не могут быть переопределены, и нет никаких хуков в его Add
/ Insert
/ Remove
операциях.
Это означает, что если вам нужно изменить поведение коллекции в будущем (например, отклонить пустые объекты, которые люди пытаются добавить, или выполнить дополнительную работу, когда это происходит, например, обновить состояние вашего класса), вам нужно изменить тип из коллекции, которую вы вернете к тому, который вы можете создать в подклассе, что будет серьезным изменением интерфейса (конечно, изменение семантики таких вещей, как недопущение нулевого значения, также может быть изменением интерфейса, но такие вещи, как обновление состояния внутреннего класса, не будут).
Таким образом, возвращая либо класс, который можно легко разделить на подклассы, например, Collection<T>
либо интерфейс, например IList<T>
, ICollection<T>
либо IEnumerable<T>
вы можете изменить внутреннюю реализацию на другой тип коллекции в соответствии с вашими потребностями, не нарушая код потребителей, поскольку он все еще может быть возвращен как тип, который они ожидают.
API простота
List<T>
содержит много полезных операций , таких как BinarySearch
, Sort
и так далее. Однако, если вы представляете эту коллекцию, скорее всего, вы контролируете семантику списка, а не потребителей. Таким образом, хотя вашему классу внутренне могут понадобиться эти операции, очень маловероятно, что потребители вашего класса захотят (или даже должны) вызвать их.
Таким образом, предлагая более простой класс коллекции или интерфейс, вы уменьшаете количество членов, которые видят пользователи вашего API, и упрощаете их использование.
Я бы лично объявил, что он возвращает интерфейс, а не конкретную коллекцию. Если вы действительно хотите получить доступ к списку, используйте
IList<T>
. В противном случае рассмотримICollection<T>
иIEnumerable<T>
.источник
We recommend using Collection<T>, ReadOnlyCollection<T>, or KeyedCollection<TKey,TItem> for outputs and properties and interfaces IEnumerable<T>, ICollection<T>, IList<T> for inputs.
CA1002, похоже , согласуется с комментариями Кшиштофа. Я не могу себе представить, почему вместо интерфейса была бы рекомендована конкретная коллекция и почему существует различие между входами / выходами.ReadOnlyCollection<T>
не имеет смысла для ввода. Точно так же,IList<T>
как входные данные говорят: «Мне нужен Sort () или какой-то другой член, который есть в IList», что не имеет смысла для выходных данных. Но я имел в виду то, что былоICollection<T>
бы рекомендовано в качестве входных данных иCollection<T>
выходных данных. Почему бы не использоватьICollection<T>
в качестве вывода также, как вы предложили?Collection<T>
иReadOnlyCollection<T>
оба происходят отICollection<T>
(то есть нетIReadOnlyCollection<T>
). Если вы вернете интерфейс, неясно, какой он есть и можно ли его изменить. В любом случае, спасибо за ваш вклад. Это была хорошая проверка здравомыслия для меня.Я не думаю, что кто-то ответил на вопрос «почему» еще ... так что здесь. Причина "почему" вы "должны" использовать
Collection<T>
вместо»,List<T>
заключается в том, что если вы выставляетеList<T>
, то любой, кто получит доступ к вашему объекту, может изменить элементы в списке. Принимая во внимание,Collection<T>
что предполагается, что вы делаете свои собственные методы «Добавить», «Удалить» и т. Д.Вам, вероятно, не нужно беспокоиться об этом, потому что вы, вероятно, кодируете интерфейс только для себя (или, возможно, нескольких коллег). Вот еще один пример, который может иметь смысл.
Если у вас есть открытый массив, например:
Можно подумать, потому что есть только метод доступа get, который никто не может связать со значениями, но это не так. Любой может изменить значения внутри, вот так:
Лично я бы просто использовал
List<T>
в большинстве случаев. Но если вы разрабатываете библиотеку классов, которую вы собираетесь раздавать случайным разработчикам, и вам нужно полагаться на состояние объектов ... тогда вы захотите создать свою собственную коллекцию и заблокировать ее оттуда: )источник
В основном речь идет об абстрагировании ваших собственных реализаций, а не о предоставлении объекта List для прямого манипулирования.
Не рекомендуется позволять другим объектам (или людям) напрямую изменять состояние ваших объектов. Подумайте о свойствах получателей / установщиков.
Коллекция -> Для обычной коллекции
ReadOnlyCollection -> Для коллекций, которые не должны быть изменены
KeyedCollection -> Если вы хотите использовать словари.
Как это исправить, зависит от того, что вы хотите, чтобы ваш класс делал, и от цели метода GetList (). Можете ли вы уточнить?
источник
ReadOnlyCollection
двух других не подчиняется этому.GetList()
как помочь ему более правильно.Collection<T>
помогает в абстрагировании внутренней реализации и предотвращает прямое манипулирование внутренним списком. Как?Collection<T>
это просто оболочка, работающая на том же переданном экземпляре. Это динамическая коллекция, предназначенная для наследования, и ничего более (ответ Грега здесь более актуален).В подобных случаях я обычно стараюсь раскрыть наименьшее количество необходимых реализаций. Если потребители не должны знать, что вы на самом деле используете список, вам не нужно возвращать список. Возвращаясь, как Microsoft предлагает коллекцию, вы скрываете тот факт, что используете список от потребителей вашего класса, и изолируете их от внутренних изменений.
источник
Что-то, чтобы добавить, хотя это было давно, так как это спросили.
Когда ваш производный тип списка из
List<T>
вместоCollection<T>
, вы не можете реализовать защищенные виртуальные методы , чтоCollection<T>
орудия. Это означает, что ваш производный тип не может отвечать в случае внесения каких-либо изменений в список. Это потому, чтоList<T>
предполагается, что вы знаете, когда вы добавляете или удаляете элементы. ВозможностьList<T>
отвечать на уведомления - это накладные расходы и, следовательно , не предлагать их.В случаях, когда внешний код имеет доступ к вашей коллекции, вы можете не контролировать, когда элемент добавляется или удаляется. Поэтому
Collection<T>
предоставляет способ узнать, когда ваш список был изменен.источник
Я не вижу проблем с возвратом чего-то вроде
Если я вернул отключенную копию внутренних данных или отсоединенный результат запроса данных - я могу безопасно вернуться,
List<TItem>
не раскрывая никаких деталей реализации, и позволить использовать возвращенные данные удобным способом.Но это зависит от того, какого типа потребителей я ожидаю - если это что-то вроде сетки данных, я предпочитаю возвращать,
IEnumerable<TItem>
который в любом случае будет скопированным списком элементов :)источник
Ну, класс Collection на самом деле является просто классом-оболочкой для других коллекций, чтобы скрыть детали их реализации и другие функции. Я считаю, что это как-то связано с шаблоном кодирования скрытия свойств в объектно-ориентированных языках.
Я думаю, вам не стоит об этом беспокоиться, но если вы действительно хотите угодить инструменту анализа кода, просто сделайте следующее:
источник
Collection<T>
Насколько я понимаю, упаковка в себя не защищает ни себя, ни основную коллекцию.Collection<T>
сразу же защитит вашу основную коллекцию.