La classe Memory<T>
è una struttura di dati introdotta in C# a partire dalla versione 7.2 nell’ottica di migliorare la gestione e l’allocazione della memoria in .NET, riducendo l’uso dello heap e migliorando le prestazioni delle applicazioni.
==Memory<T>
è una struttura utilizzata per rappresentare una regione contigua di memoria, che può essere sia sullo heap che sullo stack==. La sua caratteristica principale è di consentire l’accesso a una porzione di memoria senza copiarne i dati, riducendo così l’overhead legato all’allocazione e alla gestione della memoria sullo heap.
Memory<T>
è particolarmente utile nei contesti in cui si lavora con grandi quantità di dati, ad esempio quando si elaborano buffer o flussi di dati. Inoltre, Memory<T>
è più flessibile rispetto alla classe Span<T>
, dato che può essere utilizzata all’interno di metodi asincroni.
Analogie con Span
- Entrambe le classi consentono di lavorare con regioni contigue di memoria senza la necessità di copiare i dati.
- Sono utili per migliorare le prestazioni delle applicazioni, specialmente quando si lavora con grandi quantità di dati, come nel caso di buffer o flussi di dati.
- Forniscono API simili per accedere e manipolare i dati nella memoria.
Differenze tra Memory e Span:
Memory<T>
può essere utilizzato con metodi asincroni, mentreSpan<T>
non può. Questo perchéSpan<T>
è una struttura ref, che impedisce la cattura di variabili nello stack in una closure e quindi non è compatibile con metodi asincroni.Span<T>
è limitato allo stack, mentreMemory<T>
può rappresentare sia memoria sullo stack che sullo heap. Di conseguenza,Span<T>
ha una durata limitata e non può essere conservato in campi di classe, a differenza diMemory<T>
.Span<T>
è più leggero e veloce rispetto aMemory<T>
, a causa della sua limitazione allo stack e dell’assenza di una classe di implementazione interna.
Quando usare l’una rispetto all’altra:
- Usare
Span<T>
quando si lavora con metodi sincroni e si desidera massimizzare le prestazioni. Poiché è limitato allo stack e non può essere conservato in campi di classe, è ideale per operazioni di breve durata che non richiedono il passaggio di dati tra metodi o thread. - Usare
Memory<T>
quando si lavora con metodi asincroni o quando si desidera conservare un riferimento a una porzione di memoria in un campo di classe. Poiché può rappresentare memoria sia sullo stack che sullo heap, è più flessibile rispetto aSpan<T>
, ma potrebbe avere un leggero impatto sulle prestazioni.
Esempio
In questo esempio, viene allocato un array di interi sullo heap e successivamente viene creato un oggetto Memory<int>
che rappresenta una porzione di tale array. Il metodo PrintMemoryContent
accetta un parametro di tipo Memory<int>
e stampa il contenuto della regione di memoria rappresentata dall’oggetto. Nota che non viene effettuata alcuna copia dei dati dell’array, ma viene semplicemente condivisa la stessa regione di memoria.
Esempio asincrono
In questo esempio, stiamo leggendo un’ampia porzione di dati in modo asincrono utilizzando MemoryStream
: poiché Memory<T>
è compatibile con le operazioni asincrone, possiamo utilizzarlo per creare una vista sull’array di byte.
Se avessimo utilizzato Span<T>
, non saremmo stati in grado di utilizzare ReadAsync
e avremmo dovuto utilizzare un approccio sincrono.