Perché l'array implementa IList?


141

Vedi la definizione della classe System.Array

public abstract class Array : IList, ...

Teoricamente, dovrei essere in grado di scrivere questo pezzo ed essere felice

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

Dovrei anche essere in grado di chiamare qualsiasi metodo dall'iList

 ilist.Add(1); //exception here

La mia domanda non è perché ottengo un'eccezione, ma piuttosto perché Array implementa IList ?


22
Buona domanda Non mi è mai piaciuta l'idea di interfacce fat (questo è il termine tecnico per questo tipo di design).
Konrad Rudolph,


2
Qualcuno si preoccupa davvero di LSP? Mi sembra abbastanza accademico.
Gabe,

13
@Gabe, allora devi lavorare con codebase più grandi. L'implementazione di un comportamento (ereditando da un'interfaccia) e quindi semplicemente ignorando le cose che non ti piacciono / non possono supportare porta a puzzolenti, offuscati, casting e infine: buggy code.
Marius,

3
@Gabe è la collezione che implica la mutabilità non le sue entità contenute. Puoi rendere il tuo membro della classe di un tipo che implementa sia IRWList <> sia IReadList <>, utilizzare se come IRWList <> internamente nella tua classe ed esporlo come IReadList. Sì, devi mettere la complessità da qualche parte, ma non vedo come ciò si applichi al trascurare LSP come un ottimo principio di progettazione (non conoscevo la proprietà IsReadOnly anche se rende IList più complesso dal punto di vista dei consumatori)
Marius

Risposte:


94

Perché un array consente un accesso rapido per indice e IList/ IList<T>is sono le uniche interfacce di raccolta che lo supportano. Quindi forse la tua vera domanda è "Perché non esiste un'interfaccia per raccolte costanti con gli indicizzatori?" E a ciò non ho risposta.

Non ci sono nemmeno interfacce di sola lettura per le raccolte. E mi mancano quelli anche più di una dimensione costante con l'interfaccia degli indicizzatori.

IMO dovrebbero esserci più interfacce di raccolta (generiche) a seconda delle caratteristiche di una raccolta. E anche i nomi avrebbero dovuto essere diversi, Listperché qualcosa con un indicizzatore è davvero stupido IMO.

  • Solo enumerazione IEnumerable<T>
  • Sola lettura ma nessun indicizzatore (.Count, .Contains, ...)
  • Ridimensionabile ma nessun indicizzatore, ovvero impostato come (Aggiungi, Rimuovi, ...) corrente ICollection<T>
  • Sola lettura con indicizzatore (indicizzatore, indexof, ...)
  • Dimensione costante con indicizzatore (indicizzatore con setter)
  • Dimensione variabile con indicizzatore (Inserisci, ...) corrente IList<T>

Penso che le interfacce della collezione attuale siano di cattivo design. Ma poiché hanno proprietà che ti dicono quali metodi sono validi (e questo fa parte del contratto di questi metodi) non infrange il principio di sostituzione.


14
Grazie per la risposta. Ma preferisco lasciare la domanda così com'è. Il motivo è semplice L'interfaccia è un contratto pubblico. Se lo si implementa, è necessario implementare completamente tutti i membri, altrimenti si rompe LSP e generalmente ha un cattivo odore, non è vero?
oleksii,

16
Si rompe LSP. Se non è presente nell'elenco. Aggiungi (elemento) dovrebbe aggiungere un elemento all'elenco indipendentemente dal tipo di calcestruzzo. Tranne casi eccezionali. Nell'implementazione dell'array in viene generata un'eccezione in un caso non eccezionale, che di per sé è una cattiva pratica
Rune FS

2
@smelch mi dispiace ma hai sbagliato LSP allora. Un array non si implementa adde quindi non può essere sostituito con qualcosa che fa quando è richiesta quell'abilità.
Rune FS

7
Ammetto che tecnicamente non viola LSP solo perché la documentazione afferma che dovresti controllare le proprietà IsFixedSizee IsReadOnly, sicuramente viola il principio Tell, Don't Ask e il principio di minor sorpresa . Perché implementare un'interfaccia quando hai intenzione di generare eccezioni per 4 dei 9 metodi?
Matteo,

11
È passato del tempo dalla domanda originale. Ma ora con .Net 4.5, ci sono interfacce aggiuntive IReadOnlyList e IReadOnlyCollection .
Tobias,

43

La sezione osservazioni della documentazione per IListdice

IList è un discendente dell'interfaccia ICollection ed è l'interfaccia di base di tutti gli elenchi non generici. Le implementazioni di IList rientrano in tre categorie: sola lettura, dimensioni fisse e dimensioni variabili . Un IList di sola lettura non può essere modificato. Un IList di dimensioni fisse non consente l'aggiunta o la rimozione di elementi, ma consente la modifica di elementi esistenti. Un IList di dimensioni variabili consente l'aggiunta, la rimozione e la modifica di elementi.

Ovviamente le matrici rientrano nella categoria di dimensioni fisse, quindi dalla definizione dell'interfaccia ha senso.


4
Immagino che sarebbero finite con molte interfacce. IListFixedSize, IListReadOnly ...
Magnus,

9
questa è in realtà una buona risposta dal punto di vista della documentazione. Ma a me sembra piuttosto un trucco. Le interfacce devono essere sottili e semplici affinché una classe possa implementare tutti i membri.
oleksii,

1
@oleksii: sono d'accordo. Le interfacce e le eccezioni di runtime non sono la combinazione più elegante. In difesa di Arrayesso implementa Addesplicitamente il metodo, che riduce il rischio di chiamarlo per caso.
Brian Rasmussen,

Fino a quando non creeremo un'implementazione IListche non consente sia la modifica che l' aggiunta / rimozione. Quindi la documentazione non è più corretta. : P
Timo,

1
@Magnus: in .Net 4.5 sono disponibili interfacce aggiuntive IReadOnlyList e IReadOnlyCollection .
RBT,

17

Perché non tutti gli ILists sono mutabili (vedi IList.IsFixedSizee IList.IsReadOnly) e gli array certamente si comportano come elenchi di dimensioni fisse.

Se la tua domanda è davvero "perché implementa un'interfaccia non generica ", allora la risposta è che erano presenti prima che arrivassero i generici.


10
@oleksii: No, non rompe LSP, perché l'interfaccia IList stessa ti dice che potrebbe non essere mutabile. Se è stato infatti garantito per essere mutevole e la matrice ha detto che in caso contrario, allora si spezzerebbe la regola.
user541686,

In realtà, Array interrompe LSP in caso di generico IList<T>e non lo interrompe in caso di non generico IList: enterprisecraftsmanship.com/2014/11/22/…
Vladimir

5

È un'eredità che abbiamo dei tempi in cui non era chiaro come gestire le raccolte di sola lettura e se Array fosse di sola lettura. Esistono flag IsFixedSize e IsReadOnly nell'interfaccia di IList. Il flag IsReadOnly indica che la raccolta non può essere modificata affatto e IsFixedSize indica che la raccolta consente la modifica, ma non l'aggiunta o la rimozione di elementi.

Al momento di .Net 4.5 è stato chiaro che alcune interfacce "intermedie" sono tenuti a lavorare in sola lettura collezioni, così IReadOnlyCollection<T>e IReadOnlyList<T>sono stati introdotti.

Ecco un ottimo post sul blog che descrive i dettagli: Collezioni di sola lettura in .NET


0

La definizione dell'interfaccia di IList è "Rappresenta una raccolta non generica di oggetti a cui è possibile accedere individualmente per indice.". L'array soddisfa completamente questa definizione, quindi deve implementare l'interfaccia. L'eccezione durante la chiamata del metodo Add () è "System.NotSupportedException: la raccolta aveva dimensioni fisse" e si è verificata perché l'array non può aumentare la sua capacità in modo dinamico. La sua capacità è definita durante la creazione dell'oggetto array.


0

Avere un array implementare IList (e transitivamente, ICollection) ha semplificato il motore Linq2Objects, poiché il cast di IEnumerable su IList / ICollection avrebbe funzionato anche per gli array.

Ad esempio, un Count () finisce per chiamare Array.Length sottotitoli, poiché è trasmesso su ICollection e l'implementazione dell'array restituisce Lunghezza.

Senza questo, il motore Linq2Objects non avrebbe un trattamento speciale per gli array e funzionerebbe in modo orribile, o avrebbero bisogno di raddoppiare il codice aggiungendo il trattamento in casi speciali per gli array (come fanno per IList). Devono aver optato invece per far implementare IList da array.

Questa è la mia opinione su "Why".


0

Inoltre, dettagli di implementazione LINQ Ultimi controlli per IList, se non implementasse l'elenco avrebbero bisogno di 2 controlli che rallentassero tutte le ultime chiamate o che avessero l'ultimo su un array che prendesse O (N)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.