Язык программирования C#9 и платформа .NET5 - Троелсен Эндрю
static void SimpleBoxUnboxOperation()
{
// Создать переменную ValueType (int).
int myInt = 25;
// Упаковать int в ссылку на object.
object boxedInt = myInt;
}
Упаковку можно формально определить как процесс явного присваивания данных типа значения переменной
System.Object
Противоположная операция также разрешена и называется распаковкой (unboxing). Распаковка представляет собой процесс преобразования значения, хранящегося в объектной ссылке, обратно в соответствующий тип значения в стеке. Синтаксически операция распаковки выглядит как обычная операция приведения, но ее семантика несколько отличается. Среда CoreCLR начинает с проверки того, что полученный тип данных эквивалентен упакованному типу, и если это так, то копирует значение в переменную, находящуюся в стеке. Например, следующие операции распаковки работают успешно при условии, что лежащим в основе типом
boxedInt
int
static void SimpleBoxUnboxOperation()
{
// Создать переменную ValueType (int).
int myInt = 25;
// Упаковать int в ссылку на object.
object boxedInt = myInt;
// Распаковать ссылку обратно в int.
int unboxedInt = (int)boxedInt;
}
Когда компилятор C# встречает синтаксис упаковки/распаковки, он выпускает код CIL, который содержит коды операций
box/unbox
ildasm.exe
.method assembly hidebysig static
void '<<Main>$>g__SimpleBoxUnboxOperation|0_0'() cil managed
{
.maxstack 1
.locals init (int32 V_0, object V_1, int32 V_2)
IL_0000: nop
IL_0001: ldc.i4.s 25
IL_0003: stloc.0
IL_0004: ldloc.0
IL_0005: box [System.Runtime]System.Int32
IL_000a: stloc.1
IL_000b: ldloc.1
IL_000c: unbox.any [System.Runtime]System.Int32
IL_0011: stloc.2
IL_0012: ret
} // end of method '<Program>$'::'<<Main>$>g__SimpleBoxUnboxOperation|0_0'
Помните, что в отличие от обычного приведения распаковка обязана осуществляться только в подходящий тип данных. Попытка распаковать порцию данных в некорректный тип приводит к генерации исключения
InvalidCastException
try/catch
int
long
static void SimpleBoxUnboxOperation()
{
// Создать переменную ValueType (int).
int myInt = 25;
// Упаковать int в ссылку на object.
object boxedInt = myInt;
<b> // Распаковать в неподходящий тип данных, чтобы</b>
<b> // инициировать исключение времени выполнения.</b>
try
{
long unboxedLong = (long)boxedInt;
}
catch (InvalidCastException ex)
{
Console.WriteLine(ex.Message);
}
}
На первый взгляд упаковка/распаковка может показаться довольно непримечательным средством языка, с которым связан больше академический интерес, нежели практическая ценность. В конце концов, необходимость хранения локального типа значения в локальной переменной
object
System.Object
Давайте обратимся к практическому применению описанных приемов. Мы будем исследовать класс
System.Collections.ArrayList
ArrayList
System.Object
Add()
Insert()
Remove()
public class ArrayList : IList, ICloneable
{
...
public virtual int Add(<b>object?</b> value);
public virtual void Insert(int index, <b>object?</b> value);