Список <T> - клас і реалізує інтерфейси ICollection <T> та IEnumerable <T> . Крім того, ICollection <T> розширює інтерфейс IEnumerable <T>. Вони не взаємозамінні, принаймні, не з усіх точок зору.
Якщо у вас є Список <T>, вам гарантується, що цей об’єкт реалізує методи та властивості, необхідні для реалізації інтерфейсом ICollection <T> та IEnumerable <T>. Компілятор це знає, і вам дозволяється неявно відкидати їх на ICollection <T> або IEnumerable <T>. Однак якщо у вас є ICollection <T>, ви повинні спочатку чітко перевірити у своєму коді, чи це список <T> чи щось інше, можливо, словник <T> (де T - це KeyValuePair ) перед тим, як надати його на те, що ви бажання.
Ви знаєте, що ICollection розширює IEnumerable, тому ви можете скинути його на IEnumerable. Але якщо у вас є лише IEnumerable, знову ж таки вам не гарантується, що це список. Це може бути, але це може бути щось інше. Ви можете очікувати недійсного винятку при видачі, якщо ви намагаєтесь, наприклад, передати список <T> до словника <T>.
Тож вони не є "взаємозамінними".
Також існує багато загальних інтерфейсів, ознайомтеся з тим, що ви можете знайти в просторі імен System.Collections.Generic .
Редагувати: стосовно вашого коментаря, штраф за ефективність абсолютно не застосовується, якщо ви використовуєте Список <T> або один із інтерфейсів, які він реалізує. Вам все одно знадобиться створити новий об’єкт, перегляньте наступний код:
List<T> list = new List<T>();
ICollection<T> myColl = list;
IEnumerable<T> myEnum = list;
list , myColl і myEnum всі вказують на один і той же об'єкт. Незалежно від того, декларуєте ви це як Список, ICollection або IEnumerable, я все ще вимагаю, щоб програма створила список. Я міг би написати це:
ICollection<T> myColl = new List<T>();
myColl , під час виконання все ще є списком.
Однак це найважливіший момент ... щоб зменшити зв’язок та збільшити ремонтопридатність, завжди слід оголошувати свої змінні та параметри методу, використовуючи найменший можливий для вас знаменник, будь то інтерфейс чи абстрактний чи конкретний клас.
Уявіть, що єдине, що потрібно методу "PerformOperation" - це перерахувати елементи, виконати певну роботу та вийти, у цьому випадку вам не потрібно ще сто методів, доступних у Список <T>, вам потрібно лише те, що доступне в IEnumerable <T >, тож слід застосувати наступне:
public void PerformOperation(IEnumerable<T> myEnumeration) { ... }
Роблячи це, ви та інші розробники знаєте, що цьому методу може бути наданий будь-який об’єкт класу, що реалізує інтерфейс IEnumerable <T>. Це може бути список, словник або спеціальний клас колекції, який написав інший розробник.
Якщо навпаки, ви вказуєте, що вам явно потрібен конкретний список <T> (і хоча це в реальному житті рідко трапляється), ви та інші розробники знаєте, що це повинен бути або Список, або інший конкретний клас, що успадковує зі списку.