Udostępnij za pośrednictwem


Boxing and Unboxing (Przewodnik programowania w języku C#)

Boxing to proces przekształcania typu wartości w typ object lub w dowolny typ interfejsu zaimplementowany przez ten typ wartości. Gdy środowisko uruchomieniowe języka wspólnego (CLR) opakowuje typ wartości, zawija wartość w wystąpieniu System.Object i przechowuje ją na zarządzanej stercie. Rozpakowywanie wyodrębnia typ wartości z obiektu. Boxing jest niejawny; rozpakowywanie jest jawne. Koncepcja boksowania i rozpakowywania stanowi podstawę ujednoliconego widoku systemu typów w języku C#, w którym wartość dowolnego typu może być traktowana jako obiekt.

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

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

o obiektu można następnie rozpakować i przypisywać 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ń, boxowanie i unboxowanie są kosztownymi procesami obliczeniowymi. Gdy typ wartości jest boksowany, należy przydzielić i skonstruować nowy obiekt. W mniejszym stopniu kastenie wymagane do rozpakowania jest również obliczeniowo kosztowne. Aby uzyskać więcej informacji, zobacz Performance.

Boks

Technika boxing służy do przechowywania typów wartości w stercie zarządzanej przez mechanizm zbierania śmieci. Boxing to niejawna konwersja typu wartości do typu object lub dowolnego typu interfejsu zaimplementowanego 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ące wyrażenie niejawnie stosuje operację boxing na zmiennej i:

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

Wynikiem tej instrukcji jest utworzenie na stosie odwołania do obiektu o, które odnosi się do wartości typu intna stercie. Ta wartość jest kopią wartości typu wartości przypisanej do zmiennej i. Różnica między dwiema zmiennymi, i i o, jest przedstawiona na poniższym obrazie konwersji w boksie.

Grafika przedstawiająca różnicę między zmiennymi i oraz o.

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 przez opakowanie. 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.

        // Create an int variable
        int i = 123;
        
        // Box the value type into an object reference
        object o = i;  // boxing
        
        // Display the initial values
        Console.WriteLine($"Value of i: {i}");
        Console.WriteLine($"Value of boxed object o: {o}");
        
        // Modify the original value type
        i = 456;
        
        // Display the values after modification
        Console.WriteLine("\nAfter changing i to 456:");
        Console.WriteLine($"Value of i: {i}");
        Console.WriteLine($"Value of boxed object o: {o}");
        
        // Output:
        // Value of i: 123
        // Value of boxed object o: 123

        // After changing i to 456:
        // Value of i: 456
        // Value of boxed object o: 123

Rozpakowywanie

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

  • Sprawdzanie wystąpienia obiektu, aby upewnić się, że jest to opakowana wartość z 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 stwierdzeń:

Grafika przedstawiająca konwersję rozpakowywania.

Aby rozpakowywanie wartościowych typów powiodło się w czasie wykonywania, element, który ma być rozpakowany, musi być odwołaniem do obiektu, który został wcześniej utworzony przez zapakowanie instancji tego typu wartości. Próba rozpalokowania null powoduje NullReferenceException. Próba rozpakowania odwołania do niezgodnego typu wartości powoduje błąd InvalidCastException.

Przykład

W poniższym przykładzie pokazano przypadek nieprawidłowego rozpakowywania i wynikowego InvalidCastException. Przy użyciu try i catchpodczas wystąpienia błędu jest wyświetlany komunikat o błędzie.

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($"{e.Message} Error: Incorrect unboxing.");
        }
    }
}

Ten program wyświetla:

Specified cast is not valid. Error: Incorrect unboxing.

Jeśli zmienisz oświadczenie:

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, zapoznaj się ze specyfikacją języka C#. Specyfikacja języka jest ostatecznym źródłem informacji o składni i użyciu języka C#.

Zobacz też