2009-03-02 19 views
15

Qual è il tipo di contenitore preferito quando si restituiscono più oggetti dello stesso tipo da una funzione?Devo restituire un array o una collezione da una funzione?

È contro la buona pratica restituire un array semplice (come MyType []), o si dovrebbe avvolgere in un contenitore generico (come ICollection <MyType>)?

Grazie!

+0

(risposte uniti dalla recente duplicato) –

risposta

15

Eric Lippert ha un buon article su questo. Nel caso in cui non si possa essere disturbati a leggere l'intero articolo, la risposta è: restituire l'interfaccia.

+0

+1 avrebbe postato lo stesso link se non lo avessi già fatto. –

+0

Grande scoperta! Lo sto leggendo ora. Grazie! – Pwninstein

0

Uso di generici. È più facile interagire con altre classi di raccolte e il sistema di tipi è più in grado di aiutarti con potenziali errori.

Il vecchio stile di restituire un array era una stampella prima dei generici.

3

Restituisce sempre un tipo di interfaccia che presenta il maggior numero di funzionalità al chiamante. Quindi nel tuo caso dovrebbe essere usato ICollection<YourType>.

Qualcosa di interessante da notare è che gli sviluppatori BCL in realtà si sono sbagliati in qualche posto del framework .NET - vedere this Eric Lippert blog post per quella storia.

+0

Come dice lui, non hanno sbagliare, ma non hanno avuto generici disponibili in opzione (erano introdotto in CLR 2.0). – erikkallen

+0

So cosa state ottenendo, ma per citare l'articolo: "Vorrei darvi un esempio di dove abbiamo ottenuto quell'orrendo errore in un modo molto visibile nel quadro". :) –

6

Perché non List<T>?

Dal posto di Eric Lippert detto da altri, ho pensato che vorrei sottolineare questo:

Se ho bisogno di una sequenza userò IEnumerable<T>, se ho bisogno di una mappatura dai numeri contigui ai dati I 'll utilizzare un List<T>, se ho bisogno di una mappatura attraverso dati arbitrari userò un Dictionary<K,V>, se ho bisogno di un set io uso un HashSet<T>. Semplicemente non ho bisogno di array per niente, quindi non li uso quasi mai . Non risolvono un problema I sono migliori degli altri strumenti nella mia eliminazione .

+0

Beh, dipende da cosa devi fare con il valore restituito. IList e Elenco non hanno lo stesso set di metodi disponibili (l'elenco ha più metodi disponibili). Se non hai bisogno di uno di questi metodi, restituire IList vuoto è la soluzione migliore poiché la regola generale è quella di restituire il tipo che deduce il minor numero di restrizioni. Tuttavia c'è una grande differenza tra l'uso/argomento, variabl locale e il ritorno. quando usi qualcosa sai come è usato, quando restituisci qualcosa non puoi essere sicuro dell'uso del reso –

0

Ciò che rende il tuo codice più leggibile, manutenibile e più facile per TE. Avrei usato la matrice semplice, più semplice == la maggior parte del tempo. Anche se devo davvero vedere il contesto per dare la risposta giusta.

4
return ICollection<type> 

Il vantaggio dei tipi di restituzione generici è che è possibile modificare l'implementazione sottostante senza modificare il codice che la utilizza. Il vantaggio di restituire il tipo specifico è che è possibile utilizzare più metodi specifici del tipo.

1

Perché non IList<MyType>?

Supporta l'indicizzazione diretta che è un segno distintivo per un array senza rimuovere la possibilità di restituire un giorno List<MyType>. Se si desidera eliminare questa funzione, è probabile che si desideri restituire IEnumerable<MyType>.

5

Se la raccolta che viene restituita è di sola lettura, ovvero non si desidera che gli elementi nella raccolta vengano modificati, utilizzare IEnumerable<T>. Questa è la rappresentazione più basilare di una sequenza di sola lettura di elementi immutabili (almeno dal punto di vista dell'enumerazione stessa).

Se si desidera che sia una raccolta autonoma che può essere modificata, utilizzare ICollection<T> o IList<T>.

Ad esempio, se si desidera restituire i risultati della ricerca di un determinato set di file, quindi restituire IEnumerable<FileInfo>.

Tuttavia, se si desidera esporre i file in una directory, tuttavia, si esporrà IList/ICollection<FileInfo> poiché è logico che si desideri modificare il contenuto della raccolta.

+0

IEnumerabl non è una raccolta di sola lettura. È un'interfaccia generale per una sequenza e se gli elementi sono immutabili o meno dipende da oggetti di T immutabili o meno. qualsiasi oggetto di List è un oggetto IEnumerable ma che certamente non rende List una raccolta di sola lettura –

+0

@runefs: viene letto erroneamente. Non sto parlando dell'immutabilità delle istanze restituite, ma piuttosto dell'immutabilità della sequenza stessa. – casperOne

0

Ci sono grandi vantaggi nel favorire IEnumerable su qualsiasi altra cosa, in quanto ciò offre la massima flessibilità di implementazione e consente di utilizzare gli operatori yield return o Linq per un'implementazione pigra.

Se il chiamante vuole un List<T> invece si può semplicemente chiamare ToList() su qualunque sei tornato, e le prestazioni complessive saranno all'incirca lo stesso come se si fosse creato e restituito una nuova List<T> dal metodo.

0

L'array è dannoso, ma anche ICollection<T> è dannoso.

ICollection<T> non può garantire che l'oggetto sarà immutabile.

mia raccomandazione è di avvolgere l'oggetto di ritorno con ReadOnlyCollection<T>

12

Restituire un IEnumerable<T> utilizzando un yield return.

+0

Suggerimento interessante, non ho mai nemmeno pensato a quello. –

+0

Ho iniziato a usarlo molto, ed è davvero un codice molto elegante con LINQ. –

+0

+1 Non sono d'accordo finché non hai aggiunto "utilizzando un rendimento". I blocchi Iterator sono uno strumento molto potente che, se usato correttamente, può semplificare il codice e l'impronta della memoria. –

8

Vorrei restituire un IList<T> in modo da offrire al consumatore della vostra funzione la massima flessibilità. In questo modo se l'utente della funzione ha solo bisogno di enumerare la sequenza che può fare, ma se vuole usare la sequenza come lista può farlo anche quello.

La mia regola generale è di accettare il tipo meno restrittivo come parametro e restituire il tipo più ricco possibile. Questo è, ovviamente, un atto di bilanciamento in quanto non si desidera bloccarsi in alcuna interfaccia o implementazione particolare (ma sempre, cerca sempre di utilizzare un'interfaccia).

Questo è l'approccio meno presuntuoso che tu, lo sviluppatore dell'API, puoi prendere. Non spetta a te decidere in che modo un consumatore della tua funzione userà ciò che ti manda - ecco perché in questo caso restituiresti un IList<T> in modo da dare loro la massima flessibilità. Anche per questo stesso motivo non potresti mai presumere di sapere quale tipo di parametro ti verrà inviato da un consumatore. Se è necessario solo iterare una sequenza inviata come parametro, impostare il parametro su IEnumerable<T> anziché su List<T>.


EDIT (monossido): Dal momento che non assomiglia la questione sta per essere chiuso, voglio solo aggiungere un collegamento dal altra domanda circa questo: Why arrays are harmful

+0

Ben detto. – Shog9

1

Dipende su cosa pensi di fare con la collezione che stai restituendo.Se stai semplicemente iterando, o se vuoi solo che l'utente iterizzi, allora sono d'accordo con @Daniel, restituisci IEnumerable<T>. Se in realtà si desidera consentire operazioni basate su elenchi, tuttavia, restituirei IList<T>.

5

Un buon consiglio che ho spesso sentito citato è questo:

essere liberale in ciò che accetti, precisa in che cosa fornite.

In termini di progettazione dell'API, suggerirei di restituire un'interfaccia, non un tipo concreto.

Prendendo il metodo di esempio, mi piacerebbe riscrivere come segue:

public IList<object> Foo() 
{ 
    List<object> retList = new List<object>(); 
    // Blah, blah, [snip] 
    return retList; 
} 

La chiave è che la vostra scelta implementazione interna - di utilizzare un elenco - non è rivelato al chiamante, ma sei restituire un'interfaccia appropriata.

Le linee guida di Microsoft sullo sviluppo del framework raccomandano di non restituire tipi specifici, favorendo le interfacce. (Spiacente, non sono riuscito a trovare un collegamento per questo)

Analogamente, i parametri dovrebbero essere il più generici possibile: anziché accettare un array, accettare un oggetto IEnumerable del tipo appropriato. Questo è compatibile con gli array e con liste e altri tipi utili.

Riprendendo il metodo di esempio:

public IList<object> Foo(IEnumerable<object> bar) 
{ 
    List<object> retList = new List<object>(); 
    // Blah, blah, [snip] 
    return retList; 
} 
Problemi correlati