Коллекция <T>против списка <T>что вы должны использовать на своих интерфейсах? - PullRequest
146 голосов
/ 07 ноября 2008

Код выглядит следующим образом:

namespace Test
{
    public interface IMyClass
    {
        List<IMyClass> GetList();
    }

    public class MyClass : IMyClass
    {
        public List<IMyClass> GetList()
        {
            return new List<IMyClass>();
        }
    }
}

Когда я запускаю анализ кода, я получаю следующую рекомендацию.

Предупреждение 3 CA1002: Microsoft.Design: Измените «Список» в IMyClass.GetList (), чтобы использовать Collection, ReadOnlyCollection или KeyedCollection

Как мне это исправить и какова здесь хорошая практика?

Ответы [ 8 ]

212 голосов
/ 07 ноября 2008

Чтобы ответить на вопрос «почему» относительно того, почему бы не List<T>, причины этого - будущее и простота API.

Future корректуры

List<T> не предназначен для легкого расширения с помощью его подкласса; это разработано, чтобы быть быстрым для внутренних реализаций. Вы заметите, что методы на нем не являются виртуальными и поэтому не могут быть переопределены, и нет никаких хуков для его операций Add / Insert / Remove.

Это означает, что если вам необходимо изменить поведение коллекции в будущем (например, отклонить нулевые объекты, которые люди пытаются добавить, или выполнить дополнительную работу, когда это происходит, например, обновить состояние вашего класса), то вам необходимо измените тип коллекции, которую вы вернете, на ту, которую вы можете создать в подклассе, что будет серьезным изменением интерфейса (конечно, изменение семантики таких вещей, как недопущение нулевого значения, также может быть изменением интерфейса, но такие вещи, как обновление состояния внутреннего класса, не ).

Таким образом, возвращая либо класс, который можно легко разделить на подклассы, например Collection<T>, либо интерфейс, такой как IList<T>, ICollection<T> или IEnumerable<T>, вы можете изменить внутреннюю реализацию на другой тип коллекции, соответствующий вашим необходимо, не нарушая код потребителей, потому что он все еще может быть возвращен как ожидаемый тип.

API Простота

List<T> содержит много полезных операций, таких как BinarySearch, Sort и так далее. Однако, если вы представляете эту коллекцию, скорее всего, вы контролируете семантику списка, а не потребителей. Поэтому, хотя вашему классу могут понадобиться эти операции внутри организации, очень маловероятно, что потребители вашего класса захотят (или даже должны) вызвать их.

Таким образом, предлагая более простой класс коллекции или интерфейс, вы уменьшаете количество элементов, которые видят пользователи вашего API, и упрощаете их использование.

47 голосов
/ 07 ноября 2008

Я бы лично объявил, что он возвращает интерфейс, а не конкретную коллекцию. Если вы действительно хотите получить доступ к списку, используйте IList<T>. В противном случае рассмотрим ICollection<T> и IEnumerable<T>.

1 голос
/ 11 ноября 2015

Что-то добавить, хотя прошло много времени с тех пор, как об этом спросили.

Когда ваш тип списка происходит от List<T> вместо Collection<T>, вы не можете реализовать защищенные виртуальные методы, которые реализует Collection<T>. Это означает, что ваш производный тип не может отвечать в случае внесения каких-либо изменений в список. Это потому, что List<T> предполагает, что вы знаете, когда добавляете или удаляете элементы. Возможность отвечать на уведомления - непроизводительная нагрузка, поэтому List<T> не предлагает ее.

В случаях, когда внешний код имеет доступ к вашей коллекции, вы не можете контролировать, когда элемент добавляется или удаляется. Поэтому Collection<T> предоставляет способ узнать, когда ваш список был изменен.

1 голос
/ 07 ноября 2008

В таких случаях я обычно стараюсь раскрыть наименьшее количество необходимых реализаций. Если потребители не должны знать, что вы на самом деле используете список, вам не нужно возвращать список. Возвращаясь, как Microsoft предлагает коллекцию, вы скрываете тот факт, что используете список от потребителей вашего класса, и изолируете их от внутренних изменений.

1 голос
/ 07 ноября 2008

В основном речь идет об абстрагировании ваших собственных реализаций, а не о предоставлении объекта List для прямого манипулирования.

Не рекомендуется позволять другим объектам (или людям) напрямую изменять состояние ваших объектов. Подумайте о свойствах получателей / установщиков.

Коллекция -> Для обычной коллекции
ReadOnlyCollection -> Для коллекций, которые не следует изменять
KeyedCollection -> Когда вам нужны словари.

Как это исправить, зависит от того, что вы хотите, чтобы ваш класс делал, и от цели метода GetList (). Вы можете уточнить?

1 голос
/ 07 ноября 2008

Я не думаю, что кто-то еще ответил на вопрос "почему" ... так что здесь. Причина, по которой «вы» должны «использовать» Collection<T> вместо List<T>, заключается в том, что если вы выставите List<T>, то любой, кто получит доступ к вашему объекту, может изменить элементы в списке. Принимая во внимание, что Collection<T> должен указывать на то, что вы делаете свои собственные методы «Добавить», «Удалить» и т. Д.

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

Если у вас есть открытый массив, например:

public int[] MyIntegers { get; }

Можно подумать, что поскольку существует только метод доступа get, который никто не может связать со значениями, но это не так. Любой может изменить значения внутри себя так:

someObject.MyIngegers[3] = 12345;

Лично я бы просто использовал List<T> в большинстве случаев. Но если вы разрабатываете библиотеку классов, которую вы собираетесь раздавать случайным разработчикам, и вам нужно полагаться на состояние объектов ... тогда вы захотите создать свою собственную коллекцию и заблокировать ее оттуда: )

0 голосов
/ 14 января 2012

Я не вижу проблем с возвратом чего-то вроде

this.InternalData.Filter(crteria).ToList();

Если я вернул отключенную копию внутренних данных или отсоединенный результат запроса данных - я могу безопасно вернуть List<TItem>, не раскрывая никаких подробностей реализации, и разрешить использовать возвращенные данные в удобный способ.

Но это зависит от того, какого типа потребителей я ожидаю - если это что-то вроде сетки данных, я предпочитаю возвращать IEnumerable<TItem> , который в любом случае будет скопированным списком элементов в большинстве случаев:)

0 голосов
/ 07 ноября 2008

Что ж, класс Collection на самом деле является просто классом-оболочкой для других коллекций, чтобы скрыть детали их реализации и другие функции. Я считаю, что это как-то связано с шаблоном кодирования скрытия свойств в объектно-ориентированных языках.

Я думаю, вам не стоит об этом беспокоиться, но если вы действительно хотите угодить инструменту анализа кода, просто сделайте следующее:

//using System.Collections.ObjectModel;

Collection<MyClass> myCollection = new Collection<MyClass>(myList);
...