Lista <T> é uma classe e implementa as interfaces ICollection <T> e IEnumerable <T> . Além disso, ICollection <T> estende a interface IEnumerable <T>. Eles não são intercambiáveis, pelo menos não de todos os pontos de vista.
Se você possui uma Lista <T>, é garantido que este objeto implemente métodos e propriedades que devem ser implementados pelas interfaces ICollection <T> e IEnumerable <T>. O compilador sabe disso e você pode convertê-los "para baixo" em um ICollection <T> ou em um IEnumerable <T> implicitamente. No entanto, se você tiver uma ICollection <T>, deverá verificar explicitamente em seu código primeiro se é uma Lista <T> ou outra coisa, talvez um Dicionário <T> (onde T é um KeyValuePair ) antes de convertê-lo para o que você deseja. desejo.
Você sabe que o ICollection estende IEnumerable, para que você possa convertê-lo em um IEnumerable. Mas se você tiver apenas um IEnumerable, novamente não há garantia de que seja uma lista. Pode ser, mas poderia ser outra coisa. Você deve esperar uma exceção de conversão inválida se tentar converter uma Lista <T> em um Dicionário <T>, por exemplo.
Portanto, eles não são "intercambiáveis".
Além disso, existem muitas interfaces genéricas, confira o que você pode encontrar no espaço para nome System.Collections.Generic .
Editar: em relação ao seu comentário, não há absolutamente nenhuma penalidade no desempenho se você usar a Lista <T> ou uma das interfaces implementadas. Você ainda precisará criar um novo objeto, confira o seguinte código:
List<T> list = new List<T>();
ICollection<T> myColl = list;
IEnumerable<T> myEnum = list;
list , myColl e myEnum apontam para o mesmo objeto. Se você o declara como uma Lista ou um ICollection ou um IEnumerable, ainda estou exigindo que o programa crie uma Lista. Eu poderia ter escrito isso:
ICollection<T> myColl = new List<T>();
myColl , em tempo de execução ainda é uma lista.
No entanto , este é o ponto mais importante ... para reduzir o acoplamento e aumentar a manutenção, você sempre deve declarar suas variáveis e parâmetros de método usando o menor denominador possível, seja uma interface ou uma classe abstrata ou concreta.
Imagine que a única coisa que o método "PerformOperation" precisa é enumerar elementos, fazer algum trabalho e sair; nesse caso, você não precisa dos cem métodos disponíveis na Lista <T>, você só precisa do que está disponível no IEnumerable <T >, portanto, o seguinte deve ser aplicado:
public void PerformOperation(IEnumerable<T> myEnumeration) { ... }
Ao fazer isso, você e outros desenvolvedores sabem que qualquer objeto de uma classe que implemente a interface IEnumerable <T> pode ser atribuído a esse método. Pode ser uma classe de lista, dicionário ou coleção personalizada que outro desenvolvedor escreveu.
Se, pelo contrário, você especificar que precisa explicitamente de uma Lista concreta <T> (e, embora raramente aconteça na vida real, isso ainda pode acontecer), você e outros desenvolvedores sabem que deve ser uma Lista ou outra classe concreta herdando da lista.