Collezione contro Lista cosa dovresti usare sulle tue interfacce?

Il codice ha un aspetto simile al seguente:

namespace Test { public interface IMyClass { List GetList(); } public class MyClass : IMyClass { public List GetList() { return new List(); } } } 

Quando eseguo l’analisi del codice ottengo la seguente raccomandazione.

Avviso 3 CA1002: Microsoft.Design: modifica “Elenco” in “IMyClass.GetList ()” per utilizzare Collection, ReadOnlyCollection o KeyedCollection

Come dovrei risolvere questo e qual è la buona pratica qui?

Per rispondere alla “perché” parte della domanda sul perché non List , i motivi sono a prova di futuro e semplicità dell’API.

A prova di futuro

List non è progettato per essere facilmente estendibile sottoclassandolo; è progettato per essere veloce per implementazioni interne. Noterai che i metodi su di esso non sono virtuali e quindi non possono essere sovrascritti e non ci sono hook nelle sue operazioni Add / Insert / Remove .

Ciò significa che se è necessario modificare il comportamento della raccolta in futuro (ad esempio, per rifiutare oggetti nulli che le persone cercano di aggiungere, o per eseguire lavori aggiuntivi quando ciò accade, come ad esempio l’aggiornamento dello stato della class), è necessario modificare il tipo di raccolta si ritorna a uno si può sottoclass, che sarà un cambiamento dell’interfaccia di rottura (ovviamente cambiare la semantica di cose come non permettere null può anche essere un cambiamento di interfaccia, ma cose come aggiornare lo stato della class interna non sarebbe).

Quindi, restituendo una class che può essere facilmente sottoclassata come Collection o un’interfaccia come IList , ICollection o IEnumerable puoi cambiare la tua implementazione interna per essere un tipo di raccolta diverso da soddisfare i tuoi bisogni, senza infrangere il codice dei consumatori perché possono ancora essere restituiti come il tipo che si aspettano.

Semplicità dell’API

List contiene molte operazioni utili come BinarySearch , Sort e così via. Tuttavia, se questa è una raccolta che stai esponendo, è probabile che controlli la semantica della lista e non i consumatori. Quindi, mentre la tua class internamente potrebbe aver bisogno di queste operazioni è molto improbabile che i consumatori della tua class vorrebbero (o dovrebbero anche) chiamarle.

Pertanto, offrendo una class o un’interfaccia di raccolta più semplice, riduci il numero di membri che gli utenti delle tue API vedono e facilita l’utilizzo da parte loro.

Personalmente lo dichiarerei per restituire un’interfaccia piuttosto che una raccolta concreta. Se vuoi veramente accedere alla lista, usa IList . Altrimenti, considera ICollection e IEnumerable .

Si tratta principalmente di astrarre le proprie implementazioni invece di esporre direttamente l’object List da manipolare.

Non è una buona pratica lasciare che altri oggetti (o persone) modifichino direttamente lo stato degli oggetti. Pensa ai getter / setter della proprietà.

Collezione -> Per collezione normale
ReadOnlyCollection -> Per le raccolte che non dovrebbero essere modificate
KeyedCollection -> Quando vuoi i dizionari.

Come risolverlo dipende da cosa vuoi che faccia la tua class e lo scopo del metodo GetList (). Puoi elaborare?

In questo tipo di casi di solito cerco di esporre la minima quantità di implementazione necessaria. Se i consumatori non hanno bisogno di sapere che stai effettivamente usando una lista, allora non hai bisogno di restituire una lista. Tornando come Microsoft suggerisce una raccolta si nasconde il fatto che si sta utilizzando un elenco dai consumatori della class e li si isola contro una modifica interna.

Non penso che nessuno abbia ancora risposto alla parte “perché” … quindi ecco qui. La ragione per cui “si” dovrebbe “usare” una Collection invece di una List è perché se si espone una List , quindi chiunque abbia accesso al proprio object può modificare gli elementi nella lista. Considerando che Collection dovrebbe indicare che stai facendo i tuoi metodi “Aggiungi”, “Rimuovi”, ecc.

Probabilmente non devi preoccuparti di questo, perché probabilmente stai codificando l’interfaccia solo per te (o forse alcuni colleghi). Ecco un altro esempio che potrebbe avere senso.

Se disponi di un array pubblico, es:

 public int[] MyIntegers { get; } 

Lo si potrebbe pensare perché c’è solo una “get” accessor che nessuno può interferire con i valori, ma non è vero. Chiunque può modificare i valori al suo interno in questo modo:

 someObject.MyIngegers[3] = 12345; 

Personalmente, vorrei solo usare List nella maggior parte dei casi. Ma se stai progettando una libreria di classi che distribuirai a sviluppatori casuali, e devi fare affidamento sullo stato degli oggetti … allora dovrai creare la tua raccolta e bloccarla da lì: )

Qualcosa da aggiungere anche se è passato molto tempo da quando è stato chiesto.

Quando il tipo di lista deriva da List invece di Collection , non è ansible implementare i metodi virtuali protetti che Collection implementa. Ciò significa che il tipo derivato non può rispondere nel caso in cui vengano apportate modifiche all’elenco. Questo perché List presume che tu sappia quando aggiungi o rimuovi elementi. Essere in grado di rispondere alle notifiche è un overhead e quindi List non lo offre.

Nei casi in cui il codice esterno ha accesso alla tua raccolta, potresti non essere in grado di controllare quando un articolo viene aggiunto o rimosso. Pertanto Collection fornisce un modo per sapere quando il tuo elenco è stato modificato.

Non vedo alcun problema nel restituire qualcosa del tipo

 this.InternalData.Filter(crteria).ToList(); 

Se restituisco una copia disconnessa di dati interni, o risultato di una query di dati scollegata, posso tranquillamente restituire List senza esporre alcuno dei dettagli di implementazione e consentire di utilizzare i dati restituiti nel modo conveniente.

Ma questo dipende dal tipo di consumatore che mi aspetto – se questo è qualcosa come la griglia di dati, preferisco restituire IEnumerable che sarà comunque la lista copiata di elementi nella maggior parte dei casi 🙂

Bene, la class Collection è in realtà solo una class wrapper attorno ad altre collezioni per hide i dettagli dell’implementazione e altre funzionalità. Ritengo che questo abbia a che fare con la proprietà che nasconde il modello di codifica nei linguaggi orientati agli oggetti.

Penso che non dovresti preoccuparti, ma se vuoi davvero piacere allo strumento di analisi del codice, procedi come segue:

 //using System.Collections.ObjectModel; Collection myCollection = new Collection(myList);