Снова о создании объектных образов, восстановлении значений и System.Object
Снова о создании объектных образов, восстановлении значений и System.Object
Чтобы понять, в чем заключаются преимущества использования обобщений, следует выяснить, какие проблемы возникают у программиста без их использования. Вы должны помнить из главы 3, что платформа .NET поддерживает автоматическое преобразование объектов стека и динамической памяти с помощью операций создания объектного образа и восстановления из объектного образа. На первый взгляд, эта возможность кажется не слишком полезной и имеющей, скорее, теоретический, чем практический интерес. Но на самом деле возможность создания объектного образа и восстановления из объектного образа очень полезна тем, что она позволяет интерпретировать все, как System.Object, оставив все заботы о распределении памяти на усмотрение CLR.
Чтобы рассмотреть особенности процесса создания объектного образа, предположим, что мы создали System.Collections.ArrayList для хранения числовых (т.е. размещаемых в стеке) данных. Напомним, что все члены ArrayList обладают прототипами для получения и возвращения типов System.Object. Но вместо того, чтобы заставлять программиста вручную вкладывать размещенное в стеке целое число в соответствующую объектную оболочку, среда выполнения делает это автоматически с помощью операции создания объектного образа.
static void Main(string[] args) {
// При передаче данных члену, требующему объект, для
// характеризуемых значениями типов автоматически создается
// объектный образ.
ArrayList myInts = new ArrayList();
myInts.Add(10);
Console.ReadLine();
}
Чтобы восстановить значение из объекта ArrayList, используя индексатор типа, вы должны превратить размещенный в динамической памяти объект в размещенное в стеке целое число, используя операцию преобразования.
static void Main(string[] args) {
…
// Значение восстанавливается… и снова становится объектом!
Console.WriteLine("Значение вашего int: {0}", (int)myInts[0]);
Console.ReadLine();
}
Для представления операции создания объектного образа в терминах CIL компилятор C# использует блок box. Точно так же операция восстановления из объектного образа преобразуется в CIL-блок unbox. Вот соответствующий CIL-код для показанного выше метода Main() (этот код можно увидеть с помощью ildasm.exe).
.method private hidebysig static void Main(string[] args) cil managed {
…
box [mscorlib]System.Int32
callvirt instance int32 [mscorlib] System.Collections.ArrayList::Add(object)
pop
ldstr "Значение вашего int: {0}"
ldloc.0
ldc.i4.0
callvirt instance object [mscorlib] System.Collections.ArrayList::get_Item(int32)
unbox [mscorlib]System.Int32
ldind.i4
box [mscorlib]System.Int32
call void [mscorlib]System.Console::WriteLine(string, object)
…
}
Обратите внимание на то. что перед обращением к ArrayList.Add() размещенное в стеке значение System.Int32 преобразуется в объект, чтобы передать требуемый System.Object. Также заметьте, что при чтении из ArrayList с помощью индексатора типа (что отображается в скрытый метод get_Item()) объект System.Object восстанавливается в System.Int32 только для того, чтобы снова стать объектным образом при передаче методу Console.WriteLine().