List <T> является классом и реализует интерфейсы ICollection <T> и IEnumerable <T> . Кроме того, ICollection <T> расширяет интерфейс IEnumerable <T>. Они не являются взаимозаменяемыми, по крайней мере, не со всех точек зрения.
Если у вас есть List <T>, вы гарантируете, что этот объект реализует методы и свойства, необходимые для реализации интерфейсами ICollection <T> и IEnumerable <T>. Компилятор знает это, и вам разрешено приводить их «вниз» либо к ICollection <T>, либо к IEnumerable <T> неявно. Однако, если у вас есть ICollection <T>, вы должны сначала явно проверить в своем коде, является ли это List <T> или что-то еще, возможно, Dictionary <T> (где T - KeyValuePair ), прежде чем привести его к тому, что вы желание.
Вы знаете, что ICollection расширяет IEnumerable, поэтому вы можете преобразовать его в IEnumerable. Но если у вас есть только IEnumerable, опять же вам не гарантируется, что это список. Может быть, но это может быть что-то еще. Следует ожидать недопустимого исключения приведения, если вы попытаетесь привести, например, List <T> к словарю <T>.
Таким образом, они не являются «взаимозаменяемыми».
Кроме того, существует множество универсальных интерфейсов, посмотрите, что вы можете найти в пространстве имен System.Collections.Generic .
Отредактируйте: в отношении вашего комментария нет абсолютно никакого снижения производительности, если вы используете List <T> или один из интерфейсов, которые он реализует. Вам все еще нужно будет создать новый объект, посмотрите следующий код:
List<T> list = new List<T>();
ICollection<T> myColl = list;
IEnumerable<T> myEnum = list;
list , myColl и myEnum указывают на один и тот же объект. Независимо от того, объявляете ли вы его как List, ICollection или IEnumerable, я все еще требую, чтобы программа создала List. Я мог бы написать это:
ICollection<T> myColl = new List<T>();
myColl , во время выполнения все еще список.
Тем не менее , это самый важный момент ... чтобы уменьшить связь и повысить удобство сопровождения, вы всегда должны объявлять свои переменные и параметры метода, используя наименьший возможный для вас знаменатель, будь то интерфейс, абстрактный или конкретный класс.
Представьте, что единственное, что нужно методу «PerformOperation», это перечислить элементы, выполнить некоторую работу и выйти, в этом случае вам не нужно больше сотни методов, доступных в List <T>, вам нужно только то, что доступно в IEnumerable <T >, поэтому должно применяться следующее:
public void PerformOperation(IEnumerable<T> myEnumeration) { ... }
Делая это, вы и другие разработчики знаете, что этот метод может быть передан любому объекту класса, реализующего интерфейс IEnumerable <T>. Это может быть List, Dictionary или пользовательский класс коллекции, написанный другим разработчиком.
Если, наоборот, вы указываете, что вам явно нужен конкретный List <T> (и хотя в реальной жизни это редко бывает, это все же может произойти), вы и другие разработчики знаете, что это должен быть либо List, либо другой конкретный класс, наследующий из списка.