Колекція <T> порівняно зі списком <T>, що слід використовувати у своїх інтерфейсах?


162

Код виглядає нижче:

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 ()", щоб використовувати колекцію, ReadOnlyCollection або KeyedCollection

Як я маю це виправити і яка тут хороша практика?

Відповіді:


234

Щоб відповісти на частину питання "чому", чому ні List<T>, причини - це стійкість до майбутнього та простота API.

Майбутнє стійкість

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

Це означає, що якщо вам потрібно буде в майбутньому змінити поведінку колекції (наприклад, відхилити нульові об'єкти, які люди намагаються додати, або виконати додаткову роботу, коли це відбувається, наприклад, оновлення стану вашого класу), то вам потрібно змінити тип колекції ви повертаєтесь до одного підкласу, який буде порушувати зміну інтерфейсу (звичайно, зміна семантики таких речей, як, наприклад, не дозволяючи null, також може бути зміною інтерфейсу, але таких речей, як оновлення стану вашого внутрішнього класу, не буде).

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

Простота API

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

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


1
Ця відповідь мертва. Ще один хороший прочитання з цього приводу можна знайти тут: blogs.msdn.com/fxcop/archive/2006/04/27/…
senfo

7
Я бачу ваші перші моменти, але я не знаю, чи згоден я на вашу частину простоти API.
Борис Калленс

stackoverflow.com/a/398988/2632991 Ось і справді хороший пост про те, яка різниця між колекціями та списками.
El Mac

2
blogs.msdn.com/fxcop/archive/2006/04/27/… -> Помер . Чи є у нас новіша інформація для цього?
Мачадо

1
Робоче посилання: blogs.msdn.microsoft.com/kcwalina/2005/09/26/…
ofthelit

50

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


1
Чи розширить IList тоді інтерфейс ICollection?
бовіум

8
@Jon: Я знаю, що це старе, але чи можете ви прокоментувати те, що говорить Кшиштоф на blogs.msdn.com/b/kcwalina/archive/2005/09/26/474010.aspx ? Зокрема, його коментар, 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, схоже, йде разом із коментарями Кшиштофа. Я не можу собі уявити, чому замість інтерфейсу рекомендується конкретна колекція, і чому розмежування входів / виходів.
Нельсон Ротермель

3
@Nelson: Рідко ви хочете вимагати, щоб абоненти переходили в незмінний список, але розумно повернути його, щоб вони знали, що це безумовно незмінне. Не впевнений у інших колекціях. Було б добре детальніше.
Джон Скіт

2
Це не для конкретного випадку. Очевидно, що взагалі ReadOnlyCollection<T>не було б сенсу для вкладу. Аналогічно, IList<T>як вказує текст, "мені потрібен Sort () або інший член, який має IList", що не має сенсу для виводу. Але те, що я мав на увазі, це було б чому ICollection<T>рекомендувати як вклад і Collection<T>як вихід. Чому б не використовувати ICollection<T>як вихід також, як ви запропонували?
Нельсон Ротермель

9
Я думаю, що це стосується неоднозначності. Collection<T>і ReadOnlyCollection<T>обидва походять від ICollection<T>(тобто немає IReadOnlyCollection<T>). Якщо ви повернете інтерфейс, не очевидно, який він є і чи можна його змінювати. У будь-якому випадку, дякую за ваш внесок. Це для мене була хороша перевірка санітарності.
Нельсон Ротермель

3

Я не думаю, що ніхто ще не відповів на частину "чому" ... так ось що. Причина, чому "ви" повинні "використовувати Collection<T>замість а", List<T>полягає в тому, що якщо ви виставляєте List<T>, то кожен, хто отримує доступ до вашого об'єкта, може змінювати елементи в списку. Тоді якCollection<T> як передбачається вказувати, що ви робите власні методи "Додати", "Видалити" тощо.

Вам, мабуть, не потрібно турбуватися про це, тому що ви, мабуть, кодуєте інтерфейс лише для себе (а може, і декількох колег). Ось ще один приклад, який може мати сенс.

Якщо у вас є загальнодоступний масив, наприклад:

public int[] MyIntegers { get; }

Ви б могли подумати, що тому, що існує лише "отримати" аксесуар, який ніхто не може зіпсувати зі значеннями, але це неправда. Будь-хто може змінити значення всередині просто так:

someObject.MyIngegers[3] = 12345;

Особисто я б просто використовував List<T>у більшості випадків. Але якщо ви проектуєте бібліотеку класів, яку збираєтеся видавати випадковим розробникам, і вам потрібно покластися на стан об’єктів ... тоді ви захочете створити власну колекцію та заблокувати її звідти: )


"Якщо ви повернете Список <T> до коду клієнта, ви ніколи не зможете отримувати сповіщення, коли код клієнта модифікує колекцію." - FX COP ... Дивіться також " msdn.microsoft.com/en-us/library/0fss9skc.aspx " ... ух, здається, я все-таки не в базі :)
Тимофій Хоурі

Нічого собі - через роки я бачу, що посилання, яке я вставив у вищезгаданому коментарі, в кінці цього пункту мав цитату, тому воно не спрацювало ... msdn.microsoft.com/en-us/library/0fss9skc.aspx
Тимофій Хорі

2

Йдеться головним чином про те, щоб відмовитись від власних реалізацій замість того, щоб викривати об'єкт "Список", яким слід безпосередньо маніпулювати.

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

Колекція -> Для звичайної колекції
ReadOnlyCollection -> Для колекцій, які не слід змінювати
KeyedCollection -> Коли вам потрібні словники.

Як це виправити, залежить від того, що ви хочете робити у вашому класі та від призначення методу GetList (). Чи можете ви докладно?


1
Але Колекція <T> і KeyedCollection <,> також не читаються лише.
nawfal

@nawfal А де я сказав, що це?
чакрит

1
Ви заявляєте, що не дозволяєте іншим об'єктам (або людям) безпосередньо змінювати стан ваших об'єктів та перелічувати 3 типи колекцій. Заборона ReadOnlyCollectionінших двох не підкоряється цьому.
nawfal

Це стосується "належної практики". Правильний контекст, будь ласка. У наведеному нижче списку просто зазначена основна вимога таких типів, оскільки ОП хоче зрозуміти попередження. Тоді я продовжую розпитувати його про мету GetList(), щоб мати можливість допомогти йому правильніше.
чакрит

1
Добре, я розумію цю частину. Але все-таки міркування не є моїми. Ви кажете, що Collection<T>допомагає абстрагувати внутрішню реалізацію та запобігає безпосередньому маніпулюванню внутрішнім списком. Як? Collection<T>це просто обгортка, що працює на тому ж переданому екземплярі. Це динамічна колекція, призначена для спадкування, нічого іншого (відповідь Грега тут більш доречна).
nawfal

1

У таких випадках я, як правило, намагаюся розкрити найменший обсяг реалізації, який потрібен. Якщо споживачам не потрібно знати, що ви насправді використовуєте список, вам не потрібно повертати його. Повертаючись, як Microsoft пропонує колекцію, ви приховуєте той факт, що використовуєте список від споживачів вашого класу та ізолюєте їх від внутрішніх змін.


1

Щось додати, хоча минуло давно, як це просили.

Якщо тип списку походить від List<T>замість цього Collection<T>, ви не можете реалізувати захищені віртуальні методи, що Collection<T>реалізуються. Це означає, що отриманий тип не може відповісти, якщо в списку внесені будь-які зміни. Це тому, що List<T>передбачає, що ви знаєте, коли ви додаєте або видаляєте елементи. Можливість відповідати на сповіщення є великою вагою, а значить List<T>, не пропонує.

У випадках, коли зовнішній код має доступ до вашої колекції, ви можете не контролювати, коли елемент додається чи видаляється. Тому Collection<T>надає спосіб дізнатися, коли ваш список був змінений.


0

Я не бачу жодної проблеми з поверненням чогось подібного

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

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

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


-1

Ну а клас колекції - це просто клас обгортки навколо інших колекцій, щоб приховати деталі їх реалізації та інші функції. Я вважаю, що це має щось спільне з властивістю, що приховує шаблон кодування в об'єктно-орієнтованих мовах.

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

//using System.Collections.ObjectModel;

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

1
Вибачте, це була помилка. Я колекція менатів <MyClass>. Я дуже з нетерпінням чекаю отримати свої руки на C # 4 та загальній коваріації BTW!
Тамас Цінеге

Collection<T>Наскільки я розумію, упаковка в захист не захищає ні себе, ні базову колекцію.
nawfal

Цей фрагмент коду полягав у тому, щоб "догодити інструменту аналізу коду". Я не думаю, що @TamasCzinege десь сказав, що використання Collection<T>негайно захистить вашу базову колекцію.
чакрит
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.