Коллекция <T> и List <T>, что вы должны использовать на своих интерфейсах?

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

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

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

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

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

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

Ответ 1

Чтобы ответить на вопрос "почему" в вопросе о том, почему не List<T>, причины являются будущей проверкой и простотой API.

Будущее теплоизолирующие

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

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

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

Простота API

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

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

Ответ 2

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

Ответ 3

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

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

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

public int[] MyIntegers { get; }

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

someObject.MyIngegers[3] = 12345;

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

Ответ 4

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

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

Коллекция → Для нормальной коллекции
ReadOnlyCollection → Для коллекций, которые не должны быть изменены
KeyedCollection → Если вы хотите словари вместо этого.

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

Ответ 5

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

Ответ 6

Что-то добавить, хотя это было давно, так как это было задано.

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

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

Ответ 7

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

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

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

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

Ответ 8

Одна цель - сделать код/​​дизайн более гибким и расширяемым!

Ответ 9

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

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

//using System.Collections.ObjectModel;

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