Amo le collezioni immutabili ma spesso si sentono come una soluzione che necessita di un problema. Possono essere unexpectedly slow a causa di how they are implemented: tentano molto duramente di utilizzare la memoria in modo efficiente al costo di rendere i loop e gli indicizzatori foreach
molto più complessi dal punto di vista dell'algoritmo. Con quanta memoria è, può essere difficile giustificare questo costo. C'è una scarsità di informazioni che si possono trovare sul web in merito a usi di successo di collezioni immutabili (diverse da ImmutableArray<T>
). Nel tentativo di rettificare ciò, ho pensato di aggiungere una risposta a questa domanda di più di 3 anni su un caso in cui ho trovato ImmutableStack<T>
veramente utile.
considerare questo albero-come la struttura molto semplice:
public class TreeNode
{
public TreeNode Parent { get; private set; }
public List<TreeNode> ChildNodes { get; set; }
public TreeNode(TreeNode parent)
{
Parent = parent;
}
}
ho creato le classi che seguono questo modello di base molte, molte volte, e in pratica, è sempre lavorato per me. Più di recente, ho iniziato a fare qualcosa di simile:
public class TreeNode
{
public ImmutableStack<TreeNode> NodeStack { get; private set; }
public ImmutableStack<TreeNode> ParentStack { get { return NodeStack.IsEmpty ? ImmutableStack<TreeNode>.Empty : NodeStack.Pop(); } }
public TreeNode Parent { get { return ParentStack.IsEmpty ? null : ParentStack.Peek(); } }
public ImmutableArray<TreeNode> ChildNodes { get; set; }
public TreeNode(TreeNode parent)
{
if (parent == null)
NodeStack = ImmutableStack<TreeNode>.Empty;
else
NodeStack = parent.NodeStack.Push(this);
}
}
Per una serie di ragioni, sono venuto a preferire la memorizzazione di un ImmutableStack<T>
di TreeNode
istanze che posso attraversare alla radice, piuttosto che semplicemente un riferimento all'istanza Parent
.
- L'implementazione di
ImmutableStack<T>
ImmutableStack<T>
è fondamentalmente un elenco collegato. Ogni istanza di ImmutableStack<T>
è in realtà un nodo in un elenco collegato. Ecco perché la proprietà ParentStack
solo Pop
è la NodeStack
. ParentStack
restituirà la stessa istanza di ImmutableStack<T>
come Parent.NodeStack
.
- Se si memorizza un riferimento allo
Parent
TreeNode
, sarebbe facile creare un ciclo circolare che causerebbe operazioni come trovare il nodo radice non terminare mai e si otterrebbe un overflow dello stack o un ciclo infinito. Un ImmutableStack<T>
, d'altra parte, può essere aumentato da Pop
-ing, garantendo che terminerà.
- Il fatto che si utilizza una raccolta (
ImmutableStack<T>
) significa che è possibile evitare i loop circolari accada, in primo luogo, semplicemente facendo attenzione che la parent
TreeNode
non si verifica due volte durante la costruzione della TreeNode
.
Questo è solo per cominciare. Penso che lo ImmutableStack<T>
renda le operazioni sugli alberi più semplici e sicure. Puoi (tranquillamente) usare LINQ su di esso. Vuoi sapere se uno TreeNode
è un discendente di un altro? Si potrebbe semplicemente fare ParentStack.Any(node => node == otherNode)
Più in generale, la classe ImmutableStack<T>
è per ogni caso in cui si desidera ricevere un Stack<T>
che si può garantire non sarà mai modificato, o avete bisogno di un modo semplice per ottenere un ' "istantanea" di una Stack<T>
ma non voglio creare creare un mucchio di copie di quella pila. Se si dispone di una serie di passaggi nidificati, è possibile utilizzare uno ImmutableStack<T>
per tenere traccia dell'avanzamento e, pertanto, quando un passaggio è completato, può trasferire il lavoro al relativo passo principale. La sua natura immutabile è particolarmente utile quando stai cercando di lavorare in parallelo.
fonte
2017-07-05 22:30:01
http://blogs.msdn.com/b/ericlippert/archive/2007/12/04/immutability-in-c-part-two-a-simple-immutable-stack.aspx –
Sì, ho visto quell'articolo. Ma non fornisce una ragione per cui uno stack immutabile potrebbe essere utile in alcuni contesti. Ancora una volta, questa potrebbe essere la mia comprensione dell'immutabilità che è sbagliata. –