2015-05-16 3 views
10

Prima dell'immutabilità, IEnumerable era l'interfaccia go-in in molte API poiché questo aveva il vantaggio che l'API era insensibile al tipo effettivo dell'oggetto passato.Qual è lo schema migliore per il passaggio di Raccolte immutabili attraverso le API

public void DoSomeEnumerationsWithACollection(IEnumerable<Thing> collectionOfThings) 
{ 
    foreach (var thing in collectionOfThings) doSomethingWith(thing); 
    foreach (var thing in collectionOfThings) doSomethingElseWith(thing); 
} 

Naturalmente ci sono almeno due inconvenienti di questo:

  1. Il codice dietro l'API non può invocare l'immutabilità collectionOfThings e può verificarsi un'eccezione "raccolta modificato" o ha colpito altri altri problemi delicati.

  2. Non sappiamo se collectionOfThings è una raccolta reale o semplicemente una query differita. Supponendo che si tratti di una raccolta reale, non corriamo il rischio di degradare le prestazioni eseguendo più enumerazioni. Supponendo che si tratti di una query differita e che sia in realtà una vera raccolta, trasformarla in un elenco locale o in un'altra raccolta congelata comporta costi inutili, sebbene ci aiuti a proteggerci dal primo problema (c'è ancora una condizione di competizione durante l'operazione "ToList")). Ovviamente possiamo scrivere una piccola quantità di codice per verificarlo e provare a fare la "cosa giusta", ma questa è una seccatura fastidiosa.

Devo ammettere che non ho mai trovato uno schema soddisfacente per affrontare questo diverso dall'uso delle convenzioni di denominazione. L'approccio pragmatico sembrava essere quello IEnumerable era l'approccio di attrito più basso per il passaggio di collezioni, nonostante gli svantaggi.

Ora, con collezioni immutabili, la situazione è molto migliorata ...

public void DoSomeEnumerationsWithACollection(ImmutableList<Thing> collectionOfThings) 
{ 

Non c'è più il rischio di modifica raccolta e non c'è alcuna ambiguità circa l'impatto sulle prestazioni di molteplici enumerazioni.

Tuttavia, abbiamo apparentemente perso flessibilità sull'API poiché ora dobbiamo passare in una lista di immutabile . Se il nostro cliente avesse qualche altro tipo di collezione immutabile enumerabile, dovrebbe essere copiato in una lista ImmutableList per essere consumato anche se tutto ciò che vogliamo fare è enumerarlo.

Idealmente saremmo stati in grado di utilizzare un'interfaccia simile

public void DoSomeEnumerationsWithACollection(IImmutableEnumerable<Thing> collectionOfThings) 

ma, naturalmente, l'interfaccia non può far rispettare la semantica come immutabilità tranne che per convenzione.

Utilizzo di una classe di base potrebbe funzionare

public void DoSomeEnumerationsWithACollection(ImmutableEnumerableBase<Thing> collectionOfThings) 

tranne che è considerato di cattivo gusto per creare le classi immutabili non sigillate per timore che una sottoclasse introdurre mutevolezza. E in ogni caso, questo non è stato fatto nel BCL.

Oppure potremmo semplicemente continuare ad usare IEnumerable nel API e l'utilizzo di una convenzione di denominazione per rendere chiaro il nostro codice si basa su di una collezione immutabile da passare in.

Quindi ...la mia domanda è: quali modelli sono considerati i migliori quando si passano collezioni immutabili? È ImmutableList il "nuovo IEnumerable" una volta che iniziamo a usare l'immutabilità o c'è un modo migliore?

Aggiornamento

IReadOnlyCollection (suggerito da Yuval Itzchakov sotto) è un netto miglioramento nel corso IEnumerable ma ancora non proteggere completamente il consumatore contro i cambiamenti incontrollati della collezione. È importante notare che il codebase di Roslyn fa un uso pesante dell'immutabilità (principalmente tramite ImmutableArray) e sembra utilizzare la tipizzazione esplicita quando si passano questi metodi in altri modi sebbene ci siano un paio di posizioni dove ImmutableList s vengono passati in metodi che accettano IEnumerable.

+0

Sebbene risulti che ci sono interfacce immutabili nel BCL (IImmutableList, IImmutableSet) non sembra esserci un'interfaccia comune alle enumerabili classi immutabili. – NeilMacMullen

+0

In genere è possibile verificare se qualcosa è una raccolta reale, se implementa ICollection –

risposta

2

quali modelli sono considerati migliori quando si passano intorno a collezioni immutabili?

Penso che la risposta alla tua domanda sia IReadOnlyCollection<T>, che verrà diffusa in .NET 4.6. Passando una raccolta di sola lettura, è possibile mantenere l'immutabilità e continuare a lavorare con le raccolte regolari che implementano tale interfaccia.

+0

Thanks Yuval. Mi sembra un bel suggerimento, anche se mi chiedo quanto sia bello applicare l'immutabilità. ReadOnlyCollection ovviamente implementa l'interfaccia IReadOnlyCollection ma la documentazione MSDN (https://msdn.microsoft.com/en-us/library/ms132474(v=vs.110).aspx) rende chiaro che ReadOnlyCollection è solo un wrapper attorno a un mutabile oggetto. Pertanto l'utilizzo di IReadOnlyCollection sembra ancora consentire il passaggio di un oggetto mutabile attraverso l'API. – NeilMacMullen

+0

@NeilMacMullen Penso che passare intorno a IROC sia ovvio nel dire "questo shoyld può essere usato solo come una copia di lettura". Qualcuno potrebbe davvero tentare di modificare questa interfaccia? –

+1

Il fornitore di un IROC si sta effettivamente proteggendo contro il consumatore che ha inavvertitamente mutato la raccolta. Tuttavia, il consumatore non ottiene la stessa garanzia. In questo senso, è un contratto più debole rispetto a un'API completamente immutabile che offre garanzie sia al consumatore sia al fornitore. – NeilMacMullen