2008-11-07 3 views
128

Код выглядит следующим образом:Коллекция <T> против списка <T> Что вы должны использовать на своих интерфейсах?

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

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

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

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

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

ответ

189

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

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

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

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

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

API Простота

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

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

46

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

+1

Будет ли IList расширять интерфейс ICollection? – bovium 2008-11-07 10:31:53

+1

Да, IList распространяется ICollection . Я добавлю ссылки для документации. – 2008-11-07 10:32:38

+8

@ Jon: Я знаю, что это старо, но можете ли вы прокомментировать, что говорит Кшиштоф на http://blogs.msdn.com/b/kcwalina/archive/2005/09/26/474010.aspx? В частности его комментарий, `Мы рекомендуем использовать Коллекцию , ReadOnlyCollection или KeyedCollection для выходов и свойств и интерфейсов IEnumerable , ICollection , IList для inputs.` CA1002, кажется, идет вместе с комментариями Кшиштофа. Я не могу себе представить, почему конкретная коллекция была бы рекомендована вместо интерфейса, и почему различие между входами/выходами. – 2010-10-05 15:46:00

-1

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

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

//using System.Collections.ObjectModel; 

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

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

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

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

public int[] MyIntegers { get; } 

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

someObject.MyIngegers[3] = 12345; 

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

1

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

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

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

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

1

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

0

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

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

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

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

0

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

1

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

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

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

 Смежные вопросы

  • Нет связанных вопросов^_^