Чому масив реалізує IList?


141

Див. Визначення класу System.Array

public abstract class Array : IList, ...

Теоретично я маю змогу написати цей шматочок і бути щасливим

int[] list = new int[] {};
IList iList = (IList)list;

Я також повинен мати можливість викликати будь-який метод з iList

 ilist.Add(1); //exception here

Моє запитання не в тому, чому я отримую виняток, а в тому, чому Array реалізує IList ?


22
Добре запитання. Мені ніколи не подобалася ідея жирних інтерфейсів (це технічний термін для такого виду дизайну).
Конрад Рудольф


2
Хтось насправді хвилює ЛСП? Мені це здається досить академічним.
Гейб

13
@Gabe, тоді вам потрібно працювати з більшими кодовими базами. Реалізація поведінки (успадкування від інтерфейсу), а потім просто ігнорування речей, які вам не подобаються / не можуть підтримувати, призводить до смердючого, затуманеного, кастингу і, нарешті, коду помилок.
Маріус

3
@Gabe - це його колекція, яка передбачає незмінність не вміщених у неї сутностей. Ви можете зробити члена класу типу, який реалізує і IRWList <>, і IReadList <>, використовувати if як IRWList <> внутрішньо у вашому класі та викрити його як IReadList. Так, ви повинні поставити складності де - то, але я просто не бачу , як це відноситься до нехтуючи LSP як дуже хороший принцип конструкції (не знав про IsReadOnly власності , хоча , що робить IList більш складним з точки зору споживачів)
Marius

Відповіді:


94

Оскільки масив дозволяє швидкий доступ через індекс, і IList/ IList<T>є єдиними інтерфейсами колекції, які підтримують це. Тож, можливо, ваше справжнє запитання: "Чому немає інтерфейсу для постійних колекцій з індексаторами?" І на це я не маю відповіді.

Для колекцій також немає інтерфейсів для читання. І мені не вистачає таких, навіть більше, ніж постійний розмір з інтерфейсом індексаторів.

IMO повинно бути ще кілька (загальних) інтерфейсів колекції, залежно від особливостей колекції. І назви теж повинні були бути різними, Listбо щось із індексом справді дурне ІМО.

  • Просто перерахування IEnumerable<T>
  • Читайте лише, але без індексатора (.Count, .Contains, ...)
  • Зміна розміру, але не має індексатора, тобто встановити як (Add, Remove, ...) current ICollection<T>
  • Читайте лише за допомогою індексатора (indexer, indexof, ...)
  • Постійний розмір з індексатором (індексатор із сеттером)
  • Змінна величина з індексатором (Вставка, ...) струму IList<T>

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


14
дякую за відповідь. Але я швидше залишаю питання таким, яким є. Причина проста. Інтерфейс - це державний договір. Якщо хтось реалізує це, треба повністю реалізувати всі члени, інакше він порушує LSP і взагалі погано пахне, чи не так?
олексій

16
Це порушує LSP. Якщо його не було в списку. Додати (пункт), слід додати елемент до списку незалежно від конкретного типу. За винятком випадків винятковості. У реалізації масиву в кидках виняток у випадку, що не стосується винятків, що само по собі є поганою практикою
Rune FS

2
@smelch Вибачте, але ви неправильно отримали LSP. Масив не реалізовується addі, таким чином, не може бути замінений тим, що відбувається, коли потрібна ця здатність.
Руна ФС

7
Я визнаю, що технічно він не порушує LSP лише тому, що в документації зазначено, що ви повинні перевірити властивості IsFixedSizeта IsReadOnlyвластивості, це безумовно порушує принцип Tell, Don't Ask і принцип найменшого здивування . Навіщо застосовувати інтерфейс, коли ви просто збираєтеся викидати винятки для 4 із 9 методів?
Метью

11
Минуло деякий час з часу первісного питання. Але тепер із .Net 4.5 є додаткові інтерфейси IReadOnlyList та IReadOnlyCollection .
Тобіас

43

Розділ із зауваженнями до документації для IListтверджень

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

Очевидно, масиви потрапляють у категорію фіксованого розміру, тому за визначенням інтерфейсу це має сенс.


4
Я думаю, вони мали б багато інтерфейсів. IListFixedSize, IListReadOnly ...
Магнус

9
це насправді хороша відповідь з точки зору документації. Але для мене це скоріше схоже на злом. Інтерфейси повинні бути тонкими та простими, щоб клас реалізував усіх членів.
олексій

1
@oleksii: Я згоден. Виключення з інтерфейсів та часу виконання - не найвишуканіша комбінація. На захист Arrayвін реалізує Addметод явно, що знижує ризик викликати його випадково.
Брайан Расмуссен

Поки ми не створимо реалізацію, IListяка забороняє як модифікацію, так і додавання / видалення. Тоді документація більше не вірна. : P
Тімо

1
@Magnus - У .Net 4.5 є додаткові інтерфейси IReadOnlyList та IReadOnlyCollection .
RBT

17

Оскільки не всі ILists є змінними (див. IList.IsFixedSizeТа IList.IsReadOnly), а масиви, безумовно, ведуть себе як списки фіксованого розміру.

Якщо ваше запитання справді "чому він реалізує неоригінальний інтерфейс", то відповідь полягає в тому, що вони існували ще до того, як генерики з'явилися.


10
@oleksii: Ні, це не порушує LSP, тому що IList сам інтерфейс говорить вам, що він може не змінюватися. Якби це було насправді гарантовано мінливими і масив сказав вам інакше, то це порушило б правило.
користувач541686

Насправді, Array розбиває LSP у випадку загального IList<T>і не порушує його у разі не загального IList: enterprisecraftsmanship.com/2014/11/22/…
Володимир

5

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

На момент .Net 4.5 стало ясно , що деякі «проміжні» інтерфейси необхідні для роботи з читати тільки колекції, так IReadOnlyCollection<T>і IReadOnlyList<T>були введені.

Ось чудова публікація в блозі, що описує деталі: Читайте лише колекції в .NET


0

Визначення інтерфейсу IList - "Представляє негенеричну колекцію об'єктів, до яких можна отримати індивідуальний доступ за допомогою індексу." Масив повністю задовольняє це визначення, тому повинен реалізувати інтерфейс. Винятком при виклику методу Add () є "System.NotSupportedException: Колекція була фіксованого розміру" і сталася через те, що масив не може динамічно збільшувати свою ємність. Його ємність визначається під час створення об’єкта масиву.


0

Наявність масиву, що реалізує IList (і перехідно, ICollection), спростила двигун Linq2Objects, оскільки передача IEnumerable в IList / ICollection також буде працювати для масивів.

Наприклад, Count () закінчує виклик Array.Length під кришкою, оскільки він переданий ICollection і реалізація масиву повертає Length.

Без цього двигун Linq2Objects не мав би спеціального режиму обробки масивів і жахливо виконуватиметься, або їм потрібно буде подвоїти код, додавши обробку спеціальних випадків для масивів (як це робиться для IList). Вони, мабуть, вирішили замість цього змусити масив реалізувати IList.

Це мій погляд на "Чому".


0

Також відомості про впровадження LINQ Останні перевірки IList, якщо він не реалізує список, їм знадобиться або 2 перевірки, що сповільнюють усі Останні виклики, або Останнє в масиві, що приймає O (N)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.