Approfondimento: valori e tipi di riferimento in .Net

I tipi in Microsoft .Net possono essere un tipo di valore o un tipo di riferimento. Mentre i tipi di valore vengono generalmente archiviati nello stack, i tipi di riferimento vengono archiviati nell'heap gestito.

Un tipo di valore deriva da System.ValueType e contiene i dati all'interno della propria allocazione di memoria. In altre parole, le variabili, gli oggetti oi tipi di valore hanno la propria copia dei dati.

Un tipo di riferimento, nel frattempo, estende System.Object e punta a una posizione nella memoria che contiene i dati effettivi. Potete immaginare un tipo di riferimento simile a un puntatore che viene implicitamente dereferenziato quando vi accedete. I tipi di riferimento incorporati supportati da C # includono: oggetto, stringa e dinamico. Tutti i tipi di dati fondamentali, Boolean, Date, struct ed enum sono esempi di tipi di valore. Esempi di tipi di riferimento includono: stringhe, array, oggetti di classi, ecc. Per creare tipi di riferimento in C #, è possibile sfruttare queste parole chiave: classe, interfaccia e delegato.

Si noti che a differenza di un tipo di riferimento, non è possibile derivare da un tipo di valore, né è possibile assegnare un valore null direttamente a un tipo di valore. È possibile assegnare un valore null a un tipo di valore solo sfruttando i tipi nullable, una funzionalità aggiunta alle versioni più recenti di .Net Framework. Quando un tipo di valore viene copiato in un altro, il valore viene copiato. Quindi, puoi manipolare i valori in essi indipendentemente dall'altro: un cambiamento in uno non influisce sull'altro. Al contrario, quando copi un tipo di riferimento in un altro, il riferimento viene copiato. Se ne modifichi uno, viene influenzato anche l'altro. Ad esempio, se uno dei riferimenti è impostato su null, anche l'altro diventa null.

Posizioni di archiviazione

Il CLR archivia gli oggetti in tre tipi di posizioni di archiviazione: i registri, lo stack o l'heap gestito. Mentre gli oggetti di breve durata vengono archiviati all'interno di registri o stack, gli oggetti di lunga durata vengono archiviati nell'heap. Come accennato in precedenza, i tipi di valore sono generalmente archiviati nello stack.

È un malinteso comune che i tipi di valore siano sempre archiviati nello stack. Preferirei dire che i tipi di valore possono essere memorizzati nello stack quando la variabile è una variabile temporanea o è una variabile locale e il compilatore JIT decide di non registrare il valore. In sostanza, la posizione effettiva di un tipo di valore dipende dall'implementazione del compilatore JIT. Notare che un tipo di valore può essere memorizzato in uno stack frame, nel registro della CPU o anche nella memoria heap se il tipo di valore è contenuto all'interno di un oggetto, cioè se fa parte di un tipo di riferimento. Al contrario, i tipi di riferimento vengono archiviati nell'heap GC. Il riferimento viene memorizzato in uno stack mentre l'oggetto viene allocato nell'heap.

Le istanze oi riferimenti di un tipo di valore vengono memorizzati nello stack, nel registro o nell'heap a seconda che la durata dell'istanza o del riferimento sia di breve durata o di lunga durata. Un tipo di valore può risiedere nello stack se sono variabili locali e nell'heap gestito se sono campi di una classe, ovvero appartengono o fanno parte di un tipo di riferimento.

Passando per valore e passando per riferimento

Il listato di codice seguente illustra come passare una variabile a un metodo in base al valore.

 static void Increment(int i)

        {

            i = i + 1;

        }

        static void Main()

        {

            int x = 1;

            Increment(x);

            Console.WriteLine("The value of x is: " +x);

            Console.Read();

        }

Si noti che è possibile passare un tipo di valore come riferimento a un metodo utilizzando la parola chiave ref. Il listato di codice seguente lo illustra.

static void Increment(ref int i)

        {

            i = i + 1;

        }

        static void Main()

        {

            int x = 1;

            Increment(ref x);

            Console.WriteLine("The value of x is: " +x);

            Console.Read();

        }

Quando il codice precedente viene eseguito, nella console verrà visualizzato il messaggio "Il valore di x è: 2".

Boxe e unboxing

La conversione di un tipo di valore in un tipo di riferimento è nota come boxe. L'unboxing è esattamente l'opposto: è definito come il processo di conversione di un tipo di riferimento in un tipo di valore. Il frammento di codice seguente illustra boxing e unboxing in C #.

int i = 100;

Object obj = i; //Boxing

i = (int) obj; //Unboxing