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:
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.
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.
Sebbene risulti che ci sono interfacce immutabili nel BCL (IImmutableList, IImmutableSet) non sembra esserci un'interfaccia comune alle enumerabili classi immutabili. – NeilMacMullen
In genere è possibile verificare se qualcosa è una raccolta reale, se implementa ICollection –