Zabalení a rozbalení (Průvodce programováním v C#)

Zabalení je proces převodu typu hodnoty na typ object nebo na libovolný typ rozhraní implementovaný tímto typem hodnoty. Když pole modulu CLR (Common Language Runtime) obsahuje typ hodnoty, zabalí hodnotu uvnitř System.Object instance a uloží ji na spravovanou haldu. Rozbalení extrahuje typ hodnoty z objektu. Zabalení je implicitní; rozbalení je explicitní. Pojem zabalení a rozbalení je základem sjednoceného zobrazení systému typů, ve kterém může být hodnota libovolného typu považována za objekt.

V následujícím příkladu je proměnná typu Integer i zabalená a přiřazená objektu o .

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

Objekt o pak může být nezabalený a přiřazený celočíselné proměnné i :

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

Následující příklady ilustrují, jak se používá zabalení v jazyce 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

Výkon

Ve vztahu k jednoduchým přiřazením se zabalení a rozbalení počítají jako výpočetní náročné procesy. Když je hodnotový typ v krabici, musí být přidělen a vytvořen nový objekt. V menší míře je přetypování vyžadované pro rozbalení také nákladné výpočetní. Další informace najdete v tématu výkon.

Zabalení

Zabalení se používá k ukládání typů hodnot v haldě uvolňování paměti. Zabalení je implicitní převod typu hodnoty na typ object nebo na libovolný typ rozhraní implementovaný tímto typem hodnoty. Zabalení typu hodnoty přiděluje instanci objektu na haldě a zkopíruje hodnotu do nového objektu.

Zvažte následující deklaraci proměnné typu hodnoty:

int i = 123;

Následující příkaz implicitně aplikuje operaci zabalení na proměnnou i :

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

Výsledek tohoto příkazu vytváří odkaz o na objekt v zásobníku, který odkazuje na hodnotu typu int , na haldě. Tato hodnota je kopií hodnoty typu hodnoty přiřazené proměnné i . Rozdíl mezi dvěma proměnnými i a o , je znázorněno na následujícím obrázku převodu zabalení:

Obrázek znázorňující rozdíl mezi proměnnými i a o.

Je také možné provést zabalení explicitně jako v následujícím příkladu, ale explicitní zabalení není nikdy vyžadováno:

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

Příklad

Tento příklad převede proměnnou celého čísla i na objekt o pomocí zabalení. Pak se hodnota uložená v proměnné i změní z 123 na 456 . Příklad ukazuje, že původní typ hodnoty a zabalený objekt používají oddělené umístění paměti, a proto může ukládat jiné hodnoty.

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
*/

Rozbalení

Rozbalení je explicitní převod z typu object na typ hodnoty nebo z typu rozhraní na typ hodnoty, který implementuje rozhraní. Rozbalení operace se skládá z těchto:

  • Kontrola instance objektu, aby se zajistilo, že se jedná o zabalenou hodnotu daného typu hodnoty.

  • Zkopírování hodnoty z instance do proměnné typu hodnoty.

Následující příkazy ukazují operace zabalení a rozbalení:

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

Následující obrázek ukazuje výsledek předchozích příkazů:

Obrázek znázorňující převod rozbalení

Pro rozbalení typů hodnot, které mají být v době běhu úspěšné, musí být položka unboxed odkaz na objekt, který byl dříve vytvořen zabalením instance daného typu hodnoty. Pokus o unbox null způsobí NullReferenceException . Pokus o unbox odkazu na nekompatibilní typ hodnoty způsobí InvalidCastException .

Příklad

Následující příklad ukazuje případ neplatného rozbalení a výsledného InvalidCastException . tryPři použití a se catch zobrazí chybová zpráva, když dojde k chybě.

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);
        }
    }
}

Výstup tohoto programu:

Specified cast is not valid. Error: Incorrect unboxing.

Pokud změníte příkaz:

int j = (short)o;

na

int j = (int)o;

provede se převod a zobrazí se výstup:

Unboxing OK.

specifikace jazyka C#

Další informace najdete v tématu Specifikace jazyka C#. Specifikace jazyka je úplným a rozhodujícím zdrojem pro syntaxi a použití jazyka C#.

Viz také