Boxing e unboxing (Guida per programmatori C#)

Il boxing è il processo di conversione di un tipo di valore nel tipo object o in qualsiasi tipo di interfaccia implementato dal tipo valore. Quando il Common Language Runtime (CLR) esegue il boxing di un tipo di valore, incapsula il valore in un'istanza di System.Object e lo archivia nell'heap gestito. Mediante la conversione unboxing, invece, il tipo valore viene estratto dall'oggetto. La conversione boxing è implicita; quella unboxing è esplicita. Il concetto di conversione boxing e unboxing è alla base della visione unificata del sistema dei tipi in C#, in base alla quale un valore di qualsiasi tipo può essere considerato come un oggetto.

Nell'esempio seguente viene eseguita la conversione boxing della variabile intera i e la sua assegnazione all'oggetto o.

int i = 123;
// The following line boxes i.
object o = i;

È quindi possibile eseguire l'unboxing dell'oggetto o e la sua assegnazione alla variabile intera i:

o = 123;
i = (int)o;  // unboxing

Nell'esempio seguente viene illustrato l'utilizzo della conversione boxing in C#.

// String.Concat example.
// String.Concat has many versions. Rest the mouse pointer on
// Concat in the following statement to verify that the version
// that is used here takes three object arguments. Both 42 and
// true must be boxed.
Console.WriteLine(String.Concat("Answer", 42, true));

// List example.
// Create a list of objects to hold a heterogeneous collection
// of elements.
List<object> mixedList = new List<object>();

// Add a string element to the list.
mixedList.Add("First Group:");

// Add some integers to the list.
for (int j = 1; j < 5; j++)
{
    // Rest the mouse pointer over j to verify that you are adding
    // an int to a list of objects. Each element j is boxed when
    // you add j to mixedList.
    mixedList.Add(j);
}

// Add another string and more integers.
mixedList.Add("Second Group:");
for (int j = 5; j < 10; j++)
{
    mixedList.Add(j);
}

// Display the elements in the list. Declare the loop variable by
// using var, so that the compiler assigns its type.
foreach (var item in mixedList)
{
    // Rest the mouse pointer over item to verify that the elements
    // of mixedList are objects.
    Console.WriteLine(item);
}

// The following loop sums the squares of the first group of boxed
// integers in mixedList. The list elements are objects, and cannot
// be multiplied or added to the sum until they are unboxed. The
// unboxing must be done explicitly.
var sum = 0;
for (var j = 1; j < 5; j++)
{
    // The following statement causes a compiler error: Operator
    // '*' cannot be applied to operands of type 'object' and
    // 'object'.
    //sum += mixedList[j] * mixedList[j];

    // After the list elements are unboxed, the computation does
    // not cause a compiler error.
    sum += (int)mixedList[j] * (int)mixedList[j];
}

// The sum displayed is 30, the sum of 1 + 4 + 9 + 16.
Console.WriteLine("Sum: " + sum);

// Output:
// Answer42True
// First Group:
// 1
// 2
// 3
// 4
// Second Group:
// 5
// 6
// 7
// 8
// 9
// Sum: 30

Prestazioni

Rispetto alle semplici assegnazioni, le conversioni boxing e unboxing sono processi onerosi dal punto di vista del calcolo. La conversione boxing di un tipo valore comporta infatti l'allocazione e la costruzione di un nuovo oggetto. A un livello inferiore, anche il cast richiesto per la conversione unboxing è oneroso dal punto di vista del calcolo. Per altre informazioni, vedere Prestazioni.

Boxing

La conversione boxing viene utilizzata per archiviare tipi valore nell'heap sottoposto a Garbage Collection. Il boxing è una conversione implicita di un tipo di valore al tipo object o a qualsiasi tipo di interfaccia implementato da questo tipo di valore. La conversione boxing di un tipo valore prevede l'allocazione di un'istanza dell'oggetto nell'heap e la copia del valore nel nuovo oggetto.

Si consideri la seguente dichiarazione di una variabile di tipo valore:

int i = 123;

L'istruzione seguente applica implicitamente l'operazione di conversione boxing alla variabile i:

// Boxing copies the value of i into object o.
object o = i;

Il risultato di questa istruzione è la creazione, sullo stack, di un riferimento all'oggetto o che fa riferimento a un valore di tipo int nell'heap. Questo valore è una copia di quello di tipo valore assegnato alla variabile i. La differenza tra le due variabili i e o è illustrata nella figura seguente della conversione boxing:

Figura che mostra la differenza tra le variabili i e o.

È inoltre possibile, anche se non obbligatorio, eseguire la conversione boxing in modo esplicito, come nell'esempio seguente:

int i = 123;
object o = (object)i;  // explicit boxing

Esempio

In questo esempio viene eseguita la conversione boxing della variabile intera i in un oggetto o. Il valore archiviato nella variabile i viene quindi modificato da 123 a 456. Nell'esempio il tipo valore originale e l'oggetto sottoposto a conversione boxing utilizzano posizioni di memoria separate, pertanto possono archiviare valori diversi.

class TestBoxing
{
    static void Main()
    {
        int i = 123;

        // Boxing copies the value of i into object o.
        object o = i;

        // Change the value of i.
        i = 456;

        // The change in i doesn't affect the value stored in o.
        System.Console.WriteLine("The value-type value = {0}", i);
        System.Console.WriteLine("The object-type value = {0}", o);
    }
}
/* Output:
    The value-type value = 456
    The object-type value = 123
*/

Unboxing

L'unboxing è una conversione esplicita dal tipo object a un tipo di valore o da un tipo di interfaccia a un tipo di valore che implementa l'interfaccia. Un'operazione unboxing prevede le operazioni seguenti:

  • Controllo dell'istanza di oggetto per verificare che si tratti di un valore sottoposto a conversione boxing del tipo valore specificato.

  • Copia del valore dall'istanza alla variabile di tipo valore.

Le istruzioni seguenti illustrano operazioni di conversione boxing e unboxing:

int i = 123;      // a value type
object o = i;     // boxing
int j = (int)o;   // unboxing

La figura seguente mostra il risultato delle istruzioni precedenti:

Figura che illustra una conversione unboxing.

Per eseguire correttamente la conversione unboxing di tipi valore in fase di esecuzione, è necessario che l'elemento sottoposto a conversione unboxing faccia riferimento a un oggetto creato in precedenza tramite la conversione boxing di un'istanza di tale tipo valore. Il tentativo di eseguire la conversione unboxing di un valore null genera NullReferenceException. Il tentativo di eseguire la conversione unboxing di un riferimento a un tipo valore incompatibile genera InvalidCastException.

Esempio

Nell'esempio riportato di seguito viene illustrato un caso di unboxing non valido, nonché l'elemento InvalidCastException risultante. Se si utilizza try e catch, quando si verifica l'errore viene visualizzato un messaggio di errore.

class TestUnboxing
{
    static void Main()
    {
        int i = 123;
        object o = i;  // implicit boxing

        try
        {
            int j = (short)o;  // attempt to unbox

            System.Console.WriteLine("Unboxing OK.");
        }
        catch (System.InvalidCastException e)
        {
            System.Console.WriteLine("{0} Error: Incorrect unboxing.", e.Message);
        }
    }
}

Questo programma restituisce:

Specified cast is not valid. Error: Incorrect unboxing.

Se si modifica l'istruzione:

int j = (short)o;

in:

int j = (int)o;

la conversione verrà eseguita e si otterrà l'output:

Unboxing OK.

Specifiche del linguaggio C#

Per altre informazioni, vedere la specifica del linguaggio C#. La specifica del linguaggio costituisce il riferimento ufficiale principale per la sintassi e l'uso di C#.

Vedi anche