Pergunta

Alguém pode me explicar por que eu gostaria de usar IList em vez de List em C#?

Pergunta relacionada: Por que é considerado ruim expor List<T>

Foi útil?

Solução

Se você estiver expondo sua classe através de uma biblioteca que outras pessoas usarão, geralmente deseja expor -a por meio de interfaces em vez de implementações concretas. Isso ajudará se você decidir alterar a implementação da sua classe posteriormente para usar uma classe de concreto diferente. Nesse caso, os usuários da sua biblioteca não precisam atualizar seu código, pois a interface não muda.

Se você está apenas usando internamente, pode não se importar tanto e usando List<T> Talvez sim.

Outras dicas

A resposta menos popular é que os programadores gostam de fingir que seu software será reutilizado em todo o mundo, quando na verdade a maioria dos projetos será mantida por uma pequena quantidade de pessoas e por mais legais que sejam as frases de efeito relacionadas à interface, você está iludindo você mesmo.

Astronautas de Arquitetura.As chances de você escrever seu próprio IList que acrescente algo aos que já estão no framework .NET são tão remotas que são bolinhas teóricas reservadas para "melhores práticas".

Software astronauts

Obviamente, se lhe perguntarem o que você usa em uma entrevista, você diz IList, sorri, e ambos parecem satisfeitos por serem tão inteligentes.Ou para uma API pública, IList.Espero que você entenda meu ponto.

Interface é uma promessa (ou um contrato).

Como está sempre com as promessas - menor, melhor.

Algumas pessoas dizem "sempre usam IList<T> ao invés de List<T>".
Eles querem que você mude suas assinaturas de método de void Foo(List<T> input) para void Foo(IList<T> input).

Essas pessoas estão erradas.

É mais sutil do que isso. Se você está retornando um IList<T> Como parte da interface pública da sua biblioteca, você deixa opções interessantes para talvez fazer uma lista personalizada no futuro. Você pode nunca precisar dessa opção, mas é um argumento. Eu acho que é o argumento inteiro para devolver a interface em vez do tipo de concreto. Vale a pena mencionar, mas neste caso tem uma falha séria.

Como um discurso menor, você pode encontrar cada chamador precisa de um List<T> Enfim, e o código de chamada está cheio de .ToList()

Mas muito mais importante, Se você está aceitando um ilista como um parâmetro, é melhor ter cuidado, porque IList<T> e List<T> Não se comporte da mesma maneira. Apesar da semelhança no nome, e apesar de compartilhar um interface eles fazem não expor o mesmo contrato.

Suponha que você tenha este método:

public Foo(List<int> a)
{
    a.Add(someNumber);
}

Um colega útil "refatores" o método de aceitar IList<int>.

Seu código agora está quebrado, porque int[] implementos IList<int>, mas é de tamanho fixo. O contrato para ICollection<T> (a base de IList<T>) requer o código que o usa para verificar o IsReadOnly Sinalize antes de tentar adicionar ou remover itens da coleção. O contrato para List<T> não.

O Princípio de Substituição de Liskov (simplificado) afirma que um tipo derivado deve poder ser usado no lugar de um tipo de base, sem pré -condições ou pós -condicionas adicionais.

Parece que quebra o princípio de substituição de Liskov.

 int[] array = new[] {1, 2, 3};
 IList<int> ilist = array;

 ilist.Add(4); // throws System.NotSupportedException
 ilist.Insert(0, 0); // throws System.NotSupportedException
 ilist.Remove(3); // throws System.NotSupportedException
 ilist.RemoveAt(0); // throws System.NotSupportedException

Mas não. A resposta para isso é que o exemplo usou ilistau003CT> /Icollectionu003CT> errado. Se você usar uma icollectionu003CT> Você precisa verificar o sinalizador isreadonly.

if (!ilist.IsReadOnly)
{
   ilist.Add(4);
   ilist.Insert(0, 0); 
   ilist.Remove(3);
   ilist.RemoveAt(0);
}
else
{
   // what were you planning to do if you were given a read only list anyway?
}

Se alguém passar por você uma matriz ou uma lista, seu código funcionará bem se você verificar a bandeira todas as vezes e tiver um fallback ... mas realmente; quem faz isso? Você não sabe com antecedência se o seu método precisar de uma lista que possa levar membros adicionais; Você não especifica isso na assinatura do método? O que exatamente você faria se tivesse passado uma lista de leitura apenas como int[]?

Você pode substituir um List<T> em código que usa IList<T>/ICollection<T> corretamente. Você não podes Garanta que você pode substituir um IList<T>/ICollection<T> em código que usa List<T>.

Há um apelo ao princípio de segregação de responsabilidade única / interface em muitos argumentos para usar abstrações em vez de tipos concretos - dependem da interface mais estreita possível. Na maioria dos casos, se você estiver usando um List<T> E você acha que poderia usar uma interface mais estreita - por que não IEnumerable<T>? Isso geralmente se encaixa melhor se você não precisar adicionar itens. Se você precisar adicionar à coleção, use o tipo de concreto, List<T>.

Para mim IList<T> (e ICollection<T>) é a pior parte da estrutura .NET. IsReadOnly viola o princípio de menos surpresa. Uma aula, como Array, que nunca permite adicionar, inserir ou remover itens não deve implementar uma interface com métodos Adicionar, Inserir e Remover. (Veja também https://softwareengineering.stackexchange.com/questions/306105/implementing-an-interface-when-you-dont-need-one-of-the-properties)

É IList<T> Uma boa opção para sua organização? Se um colega pedir que você altere uma assinatura de método para usar IList<T> ao invés de List<T>, pergunte a eles como eles adicionariam um elemento a um IList<T>. Se eles não sabem sobre IsReadOnly (E a maioria das pessoas não), então não use IList<T>. Sempre.


Observe que a bandeira do IsReadonly vem do ICOLLECTIONu003CT> e indica se os itens podem ser adicionados ou removidos da coleção; Mas apenas para realmente confundir as coisas, isso não indica se elas podem ser substituídas, o que, no caso de matrizes (que retornam o isreadonlys == true) podem ser.

Para mais informações sobre o isreadonly, veja Definição MSDN de iCollectionu003CT> .Isreadonly

List<T> é uma implementação específica de IList<T>, que é um recipiente que pode ser abordado da mesma maneira que uma matriz linear T[] usando um índice inteiro. Quando você especificar IList<T> Como o tipo de argumento do método, você especifica apenas que precisa de certos recursos do contêiner.

Por exemplo, a especificação da interface não aplica uma estrutura de dados específica a ser usada. A implementação de List<T> acontece com o mesmo desempenho para acessar, excluir e adicionar elementos como uma matriz linear. No entanto, você pode imaginar uma implementação apoiada por uma lista vinculada, para a qual adicionar elementos ao final é mais barato (tempo constante), mas o acesso aleatório muito mais caro. (Observe que o .NET LinkedList<T> faz não implemento IList<T>.)

Este exemplo também informa que pode haver situações em que você precisa especificar a implementação, não a interface, na lista de argumentos: neste exemplo, sempre que você precisar de uma característica de desempenho de acesso específica. Isso geralmente é garantido para uma implementação específica de um contêiner (List<T> Documentação: "Ele implementa o IList<T> interface genérica usando uma matriz cujo tamanho é aumentado dinamicamente conforme necessário. ").

Além disso, convém expor a menor funcionalidade necessária. Por exemplo. Se você não precisa alterar o conteúdo da lista, provavelmente deve considerar usar IEnumerable<T>, que IList<T> estende -se.

Eu mudaria um pouco a pergunta, em vez de justificar por que você deve usar a interface sobre a implementação concreta, tente justificar por que você usaria a implementação concreta em vez da interface. Se você não puder justificá -lo, use a interface.

Ilistu003CT> é uma interface para que você possa herdar outra classe e ainda implementar o ilistau003CT> enquanto herdando a listau003CT> impede que você faça isso.

Por exemplo, se houver uma classe A e sua classe B herda, você não pode usar a listau003CT>

class A : B, IList<T> { ... }

Um princípio de TDD e OOP geralmente está programando para uma interface e não uma implementação.

Nesse caso específico, pois você está falando sobre uma construção de idiomas, não um personalizado, geralmente não importa, mas diz que, por exemplo, que você encontrou que a lista não suportou algo que você precisava. Se você tivesse usado o ILIST no restante do aplicativo, poderia estender a lista com sua própria classe personalizada e ainda poder passar isso sem refatorar.

O custo para fazer isso é mínimo, por que não economizar a dor de cabeça mais tarde? É disso que se trata o princípio da interface.

public void Foo(IList<Bar> list)
{
     // Do Something with the list here.
}

Nesse caso, você pode passar em qualquer classe que implemente o ilistau003CBar> interface. Se você usou listau003CBar> Em vez disso, apenas uma listau003CBar> A instância pode ser aprovada.

O ilistau003CBar> Way está mais vagamente acoplado do que a listau003CBar> caminho.

O caso mais importante para o uso de interfaces sobre as implementações está nos parâmetros da sua API. Se sua API usar um parâmetro de lista, qualquer pessoa que o use precisará usar a lista. Se o tipo de parâmetro for ilista, o chamador terá muito mais liberdade e pode usar classes que você nunca ouviu falar, o que pode nem existir quando seu código foi gravado.

Supõindo que nenhuma dessas listas versus perguntas (ou respostas) menciona a diferença de assinatura. (É por isso que procurei essa pergunta sobre isso!)

Então, aqui estão os métodos contidos pela lista que não são encontrados no ILIST, pelo menos a partir do .NET 4.5 (por volta de 2015)

  • AddRange
  • ASREADONLY
  • BininarySearch
  • Capacidade
  • Converter tudo
  • Existe
  • Achar
  • Encontrar tudo
  • FindIndex
  • Findlast
  • FindLastIndex
  • Para cada
  • GetRange
  • InserTrange
  • LastIndexOF
  • Deletar tudo
  • Removerman
  • Marcha ré
  • Ordenar
  • ToArray
  • TRIMEXCESS
  • Trueforall

E se .NET 5.0 substituir System.Collections.Generic.List<T> para System.Collection.Generics.LinearList<T>. .NET sempre possui o nome List<T> Mas eles garantem isso IList<T> é um contrato. Portanto, o IMHO nós (pelo menos) não devemos usar o nome de alguém (embora seja .NET neste caso) e termos problemas mais tarde.

Em caso de usar IList<T>, o interlocutor é sempre as coisas guardas para trabalhar, e o implementador é livre para alterar a coleção subjacente para qualquer implementação concreta alternativa de IList

Todos os conceitos são basicamente declarados na maioria das respostas acima sobre por que o uso da interface sobre implementações concretas.

IList<T> defines those methods (not including extension methods)

IList<T> Link msdn

  1. Adicionar
  2. Claro
  3. Contém
  4. Copiar para
  5. Getenumerator
  6. Índice de
  7. Inserir
  8. Remover
  9. Removeat

List<T> implementa esses nove métodos (sem incluir métodos de extensão), além disso, possui cerca de 41 métodos públicos, que pesam na sua consideração de qual usar em seu aplicativo.

List<T> Link msdn

Você faria porque definir um ilista ou uma iCollection abriria para outras implementações de suas interfaces.

Você pode querer ter um IDERRepository que defina uma coleção de pedidos em um ilista ou iCollection. Você pode ter diferentes tipos de implementações para fornecer uma lista de pedidos, desde que eles estejam em conformidade com as "regras" definidas pelo seu ilista ou iCollection.

A interface garante que você pelo menos obtenha os métodos que está esperando; estar ciente da definição da interface, isto é. Todos os métodos abstratos que devem ser implementados por qualquer classe que herde a interface. Portanto, se alguém faz uma classe enorme com vários métodos além dos que ele herdou da interface para alguma funcionalidade de adição, e esses não têm utilidade para você, é melhor usar uma referência a uma subclasse (neste caso o interface) e atribua o objeto de classe concreto a ele.

Vantagem adicional é que seu código está protegido de quaisquer alterações em classe de concreto, pois você está assinando apenas alguns dos métodos de classe de concreto e esses são os que estarão lá enquanto a classe concreta herdar da interface que você for usando. Portanto, sua segurança para você e liberdade ao codificador que está escrevendo implementação concreta para mudar ou adicionar mais funcionalidade à sua classe concreta.

Ilist <> é quase sempre preferível conforme o conselho do outro pôster, no entanto, observe há um bug no .net 3.5 sp 1 Ao executar um ilist <> através de mais de um ciclo de serialização / deserialização com o WCF DatacontractSerializer.

Agora há um SP para corrigir este bug: KB 971030

Você pode observar esse argumento de vários ângulos, incluindo a de uma abordagem puramente OO que diz para programar uma interface e não uma implementação. Com esse pensamento, o uso do ILIST segue o mesmo principal que passar e usar interfaces que você define do zero. Eu também acredito nos fatores de escalabilidade e flexibilidade fornecidos por uma interface em geral. Se uma classe implementando ilistau003CT> precisa ser estendido ou alterado, o código de consumo não precisa mudar; Ele sabe para que a interface ilista contrata. No entanto, usando uma implementação e lista concretau003CT> Em uma classe que muda, pode fazer com que o código de chamada também precise ser alterado. Isso ocorre porque uma classe aderindo ao ilistau003CT> garante um certo comportamento que não é garantido por um tipo de concreto usando a listau003CT> .

Também tendo o poder de fazer algo como modificar a implementação padrão da listau003CT> em uma classe implementando ilistau003CT> pois, por exemplo muito de flexibilidade e poder, caso contrário predefinidos pela listau003CT>

Normalmente, uma boa abordagem é usar o ILIST na sua API do público (quando apropriado, e a semântica da lista é necessária) e, em seguida, listar internamente para implementar a API. Isso permite que você mude para uma implementação diferente do ILIST sem quebrar código que usa sua classe.

A lista de nomes da classe pode ser alterada no próximo .NET Framework, mas a interface nunca mudará como a interface é o contrato.

Observe que, se sua API for usada apenas em loops foreach, etc., você poderá considerar expor apenas o iEnumerable.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top