Por que array implementa IList?

141

Veja a definição da classe System.Array

public abstract class Array : IList, ...

Teoricamente, eu deveria ser capaz de escrever esse trecho e ser feliz

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

Eu também devo chamar qualquer método do iList

 ilist.Add(1); //exception here

Minha pergunta não é por que recebo uma exceção, mas por que a matriz implementa o IList ?

oleksii
fonte
22
Boa pergunta Eu nunca gostei da idéia de interfaces gordas (esse é o termo técnico para esse tipo de design).
Konrad Rudolph
4
@ Henk, consulte: blogs.msdn.com/b/bclteam/archive/2004/11/19/267089.aspx
Anthony Pegram
2
Alguém realmente se importa com LSP? Parece bastante acadêmico para mim.
Gabe
13
@ Gabriel, então você precisa trabalhar com bases de código maiores. Implementar um comportamento (herdado de uma interface) e simplesmente ignorar as coisas que você não gosta / não pode suportar leva a mau cheiro, ofuscação, elenco e, finalmente, código de buggy.
Marius
3
@ Gabe é a coleção que implica mutabilidade e não suas entidades contidas. Você pode tornar seu membro de classe de um tipo que implementa IRWList <> e IReadList <>, use se como IRWList <> internamente em sua classe e expô-lo como IReadList. Sim, você precisa colocar a complexidade em algum lugar, mas não vejo como isso se aplica ao desconsiderar o LSP como um princípio de design muito bom (não sabia sobre a propriedade IsReadOnly, o que torna o IList mais complexo do ponto de vista dos consumidores)
Marius

Respostas:

94

Como uma matriz permite acesso rápido por índice, e IList/ IList<T>is são as únicas interfaces de coleção que suportam isso. Portanto, talvez sua pergunta real seja "Por que não há interface para coleções constantes com indexadores?" E para isso eu não tenho resposta.

Também não há interfaces somente leitura para coleções. E sinto falta daqueles ainda mais do que um tamanho constante com a interface de indexadores.

Na IMO, deve haver várias outras interfaces de coleta (genéricas), dependendo dos recursos de uma coleção. E os nomes deveriam ter sido diferentes também, Listpois algo com um indexador é realmente IMO estúpido.

  • Apenas enumeração IEnumerable<T>
  • Somente leitura, mas nenhum indexador (.Count, .Contains, ...)
  • Redimensionável, mas sem indexador, ou seja, definido como (Adicionar, Remover, ...) ICollection<T>
  • Somente leitura com indexador (indexador, indexof, ...)
  • Tamanho constante com indexador (indexador com setter)
  • Tamanho variável com indexador (Insert, ...) atual IList<T>

Eu acho que as interfaces de coleção atuais são de design ruim. Mas, como eles têm propriedades informando quais métodos são válidos (e isso faz parte do contrato desses métodos), não infringe o princípio da substituição.

CodesInChaos
fonte
14
obrigado pela resposta. Mas prefiro deixar a pergunta como está. O motivo é simples. Interface é um contrato público. Se alguém o implementa, deve-se implementar totalmente todos os membros; caso contrário, quebra o LSP e geralmente cheira mal, não é?
Oleksii
16
Ele quebra o LSP. Se não listou. Add (item) deve adicionar um item à lista, independentemente do tipo concreto. Exceto para casos excepcionais. Na implementação matriz na lança uma exceção em um caso de não-exceptionel, que em si mesma é uma prática ruim
Rune FS
2
@smelch Sinto muito, mas você entendeu errado o LSP. Uma matriz não implementa adde, portanto, não pode ser substituída por algo que ocorre quando essa capacidade é necessária.
Rune FS
7
Eu admito que tecnicamente não viola o LSP apenas porque a documentação afirma que você deve verificar as propriedades IsFixedSizee IsReadOnly, definitivamente viola o princípio Tell, Don't Ask e o princípio da menor surpresa . Por que implementar uma interface quando você está lançando exceções para 4 dos 9 métodos?
Matthew
11
Algum tempo se passou desde a pergunta original. Mas agora com o .Net 4.5, há interfaces adicionais IReadOnlyList e IReadOnlyCollection .
8263 Tobias
43

A seção de comentários da documentação para IListdiz

IList é um descendente da interface ICollection e é a interface base de todas as listas não genéricas. As implementações do IList se enquadram em três categorias: somente leitura, tamanho fixo e tamanho variável . Um IList somente leitura não pode ser modificado. Um IList de tamanho fixo não permite a adição ou remoção de elementos, mas permite a modificação de elementos existentes. Um IList de tamanho variável permite a adição, remoção e modificação de elementos.

Obviamente, as matrizes se enquadram na categoria de tamanho fixo; portanto, pela definição da interface, faz sentido.

Brian Rasmussen
fonte
4
Eu acho que eles teriam terminado com muitas interfaces. IListFixedSize, IListReadOnly ...
Magnus
9
essa é realmente uma boa resposta do ponto de vista da documentação. Mas para mim parece um hack. As interfaces devem ser finas e simples para que uma classe implemente todos os membros.
Oleksii
1
@oleksii: Eu concordo. Interfaces e exceções de tempo de execução não são a combinação mais elegante. Em defesa Arraydisso, implementa Addexplicitamente o método, o que reduz o risco de chamá-lo por acidente.
Brian Rasmussen
Até criarmos uma implementação IListque não permita modificação e adição / remoção. Então a documentação não está mais correta. : P
Timo
1
@ Magnus - No .Net 4.5, existem interfaces adicionais IReadOnlyList e IReadOnlyCollection .
RBT
17

Porque nem todos os ILists são mutáveis (veja IList.IsFixedSizee IList.IsReadOnly) e as matrizes certamente se comportam como listas de tamanho fixo.

Se sua pergunta é realmente "por que ela implementa uma interface não genérica ", a resposta é que elas existiam antes dos genéricos aparecerem.

user541686
fonte
10
@oleksii: Não, não quebra o LSP, porque a IList própria interface diz que pode não ser mutável. Se fosse de fato garantido para ser mutável e a matriz dito o contrário, em seguida, ele iria quebrar a regra.
User541686
Na verdade, o Array quebra o LSP no caso de genérico IList<T>e não o quebra no caso de não-genérico IList: enterprisecraftsmanship.com/2014/11/22/…
Vladimir
5

É um legado que temos desde os tempos em que não estava claro como lidar com coleções somente leitura e se a matriz é ou não somente leitura. Existem sinalizadores IsFixedSize e IsReadOnly na interface IList. O sinalizador IsReadOnly significa que a coleção não pode ser alterada e IsFixedSize significa que a coleção permite a modificação, mas não a adição ou remoção de itens.

Na época do .Net 4.5, estava claro que algumas interfaces "intermediárias" são necessárias para trabalhar com coleções somente leitura, IReadOnlyCollection<T>e IReadOnlyList<T>foram introduzidas.

Aqui está uma excelente postagem no blog que descreve os detalhes: Coleções somente leitura no .NET

Vladimir
fonte
0

A definição da interface IList é "Representa uma coleção não genérica de objetos que podem ser acessados ​​individualmente pelo índice". A matriz satisfaz completamente essa definição, portanto deve implementar a interface. A exceção ao chamar o método Add () é "System.NotSupportedException: Collection era de um tamanho fixo" e ocorreu porque a matriz não pode aumentar sua capacidade dinamicamente. Sua capacidade é definida durante a criação do objeto de matriz.

meir
fonte
0

Ter uma matriz implementando IList (e transitivamente, ICollection) simplificou o mecanismo Linq2Objects, pois converter o IEnumerable para IList / ICollection também funcionaria para matrizes.

Por exemplo, um Count () acaba chamando Array.Length por baixo do capô, pois é convertido para ICollection e a implementação do array retorna Length.

Sem isso, o mecanismo Linq2Objects não teria tratamento especial para matrizes e apresentaria um desempenho horrível, ou eles precisariam dobrar o código, adicionando tratamento de caso especial para matrizes (como fazem para IList). Eles devem ter optado por fazer o array implementar o IList.

Essa é a minha opinião sobre "Por que".

Herman Schoenfeld
fonte
0

Também detalha detalhes da implementação LINQ Últimas verificações para IList, se não implementasse a lista, elas precisariam de 2 verificações que atrasam todas as últimas chamadas ou têm a última em uma matriz recebendo O (N)

user1496062
fonte