Konwersja boxing i konwersja unboxing (Przewodnik programowania w języku C#)

Boxing to proces konwertowania typu wartości na typobject lub do dowolnego typu interfejsu zaimplementowanego przez ten typ wartości. Gdy środowisko uruchomieniowe języka wspólnego (CLR) poleci typ wartości, opakowuje wartość w wystąpieniu i przechowuje ją na zarządzanym System.Object stercie. Rozpakowywanie wyodrębnia typ wartości z obiektu. Boxing jest niejawny; rozpiętywanie jest jawne. Koncepcja boksowania i rozpakuniania łączy ujednolicony widok języka C# systemu typów, w którym wartość dowolnego typu może być traktowana jako obiekt.

W poniższym przykładzie zmienna i całkowita jest w polu i przypisana do obiektu o.

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

Następnie obiekt o można rozpakować i przypisać do zmiennej całkowitej i:

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

Poniższe przykłady ilustrują sposób użycia boksu w języku 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

Wydajność

W odniesieniu do prostych przypisań, boxing i rozpboxing są procesami kosztownymi obliczeniami. W przypadku pola typu wartości należy przydzielić i skonstruować nowy obiekt. W mniejszym stopniu rzutowanie wymagane do rozpakowania jest również kosztowne w obliczeniach. Aby uzyskać więcej informacji, zobacz Wydajność.

Konwersja boxing

Boxing służy do przechowywania typów wartości w stercie zbieranym przez śmieci. Boxing to niejawna konwersja typu wartości na typobject lub dowolny typ interfejsu zaimplementowany przez ten typ wartości. Typ wartości boxing przydziela wystąpienie obiektu na stercie i kopiuje wartość do nowego obiektu.

Rozważ następującą deklarację zmiennej typu wartości:

int i = 123;

Następująca instrukcja niejawnie stosuje operację boxing w zmiennej i:

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

Wynikiem tej instrukcji jest utworzenie odwołania odo obiektu na stosie, który odwołuje się do wartości typu int, na stercie. Ta wartość jest kopią wartości typu wartości przypisanej do zmiennej i. Różnica między dwiema zmiennymi i i o, przedstawiono na poniższej ilustracji konwersji boksu:

Graphic showing the difference between i and o variables.

Istnieje również możliwość jawnego wykonania boksu, jak w poniższym przykładzie, ale jawne boxing nigdy nie jest wymagane:

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

Przykład

W tym przykładzie zmienna całkowita i jest konwertowana na obiekt o przy użyciu boxingu. Następnie wartość przechowywana w zmiennej i jest zmieniana z 123 na 456. W przykładzie pokazano, że oryginalny typ wartości i poletowany obiekt używają oddzielnych lokalizacji pamięci, a zatem mogą przechowywać różne wartości.

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

Rozpakowywanie to jawna konwersja typu na typ object wartości lub typ interfejsu na typ wartości, który implementuje interfejs. Operacja rozpakowania składa się z następujących elementów:

  • Sprawdzanie wystąpienia obiektu w celu upewnienia się, że jest to pole wartości danego typu wartości.

  • Kopiowanie wartości z wystąpienia do zmiennej typu wartości.

Poniższe instrukcje przedstawiają operacje boksowania i rozpasania:

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

Na poniższej ilustracji przedstawiono wynik poprzednich instrukcji:

Graphic showing an unboxing conversion.

Aby rozpakowywanie typów wartości powiodło się w czasie wykonywania, element, który jest rozpakowany, musi być odwołaniem do obiektu, który został wcześniej utworzony przez utworzenie w polu wystąpienia tego typu wartości. Próba rozpalokowania null powoduje, że element NullReferenceException. Próba rozpakowania odwołania do niezgodnego typu wartości powoduje, że element InvalidCastException.

Przykład

W poniższym przykładzie pokazano przypadek nieprawidłowego rozpasania i wynikowego InvalidCastException. W przypadku wystąpienia błędu podczas wystąpienia błędu jest wyświetlany komunikat o błędzie i trycatch.

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

Te dane wyjściowe programu:

Specified cast is not valid. Error: Incorrect unboxing.

Jeśli zmienisz instrukcję:

int j = (short)o;

na:

int j = (int)o;

zostanie wykonana konwersja, a dane wyjściowe zostaną wyświetlone:

Unboxing OK.

specyfikacja języka C#

Aby uzyskać więcej informacji, zobacz Specyfikacja języka C#. Specyfikacja języka jest ostatecznym źródłem informacji o składni i użyciu języka C#.

Zobacz też