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
using System;
class Program
{
static void Main()
{
// Allocazione di un array di interi sullo heap
int[] numbers = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
// Creazione di un'istanza di Memory<T> che rappresenta una porzione dell'array
Memory<int> memory = numbers.AsMemory(2, 5);
// Esempio di utilizzo di un metodo che lavora su Memory<T>
PrintMemoryContent(memory);
}
static void PrintMemoryContent(Memory<int> memory)
{
// Ottenimento di uno Span<T> dall'oggetto Memory<T>
Span<int> span = memory.Span;
// Stampa del contenuto dello Span<T>
for (int i = 0; i < span.Length; i++)
{
Console.WriteLine(span[i]);
}
}
}
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.
static async Task Main()
{
// Creiamo un array di byte di grandi dimensioni
byte[] data = new byte[100_000];
// Inizializziamo l'array con dati casuali
var random = new Random();
random.NextBytes(data);
// Utilizziamo Memory<T> per creare una vista sull'array di byte
Memory<byte> memoryData = data;
// Leggiamo una parte dell'array in modo asincrono
byte[] buffer = new byte[10_000];
await ReadBytesAsync(memoryData, buffer);
// Facciamo qualcosa con il buffer, ad esempio scriverlo su console
for (int i = 0; i < buffer.Length; i++)
{
Console.Write(buffer[i] + " ");
}
}
static async Task ReadBytesAsync(Memory<byte> memoryData, byte[] buffer)
{
// Creiamo uno stream di memoria basato sull'array di byte
using var memoryStream = new MemoryStream(memoryData.ToArray());
// Leggiamo una parte dell'array in modo asincrono
await memoryStream.ReadAsync(buffer, 0, buffer.Length);
}