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 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
, jest przedstawiona na poniższym obrazie konwersji w boksie.
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ń:
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 catch
podczas 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#
Zobacz też
- typy odwołań
- typy wartości