Глава.5 .Примитивные,.ссылочные.и.значимые.типы
В некоторых языках, например в C++/CLI, разрешается распаковать упакован-
ный значимый тип, не копируя поля. Распаковка возвращает адрес неупакованной
части упакованного объекта (дополнительные члены — указатель на типовой объект
и индекс блока синхронизации — игнорируются). Затем, используя полученный
указатель, можно манипулировать полями неупакованного экземпляра (который
находится в упакованном объекте в куче). Например, реализация приведенного
выше кода на C++/CLI существенно повысит его производительность, потому что
вы можете изменить значение поля
x
структуры
Point
в уже упакованном экзем-
пляре
Point
. Это позволит избежать как выделения памяти для нового объекта,
так и повторного копирования всех полей!
ВниМание
Если.вы.хотя.бы.в.малейшей.степени.заботитесь.о.производительности.своего.
приложения,.вам.необходимо.знать,.когда.компилятор.создает.код,.выполняющий.
эти.операции .К.сожалению,.многие.компиляторы.неявно.генерируют.код.упаковки,.
поэтому.иногда.бывает.сложно.узнать.о.происходящей.упаковке .Если.меня.действи-
тельно.волнует.производительность.приложения,.я.прибегаю.к.такому.инструменту,.
как.ILDasm exe,.просматриваю.IL-код.готовых.методов.и.смотрю,.присутствуют.ли.
в.нем.команды.упаковки
Рассмотрим еще несколько примеров, демонстрирующих упаковку и распа-
ковку:
public static void Main() {
Int32 v = 5; // Создание неупакованной переменной значимого типа o
Object o = v; // указывает на упакованное Int32, содержащее 5
v = 123; // Изменяем неупакованное значение на 123
Console.WriteLine(v + ", " + (Int32) o); // Отображается "123, 5"
}
Сколько в этом коде операций упаковки и распаковки? Вы не поверите — целых
три! Разобраться в том, что здесь происходит, нам поможет IL-код метода
Main
.
Чтобы быстрее найти отдельные операции, я снабдил распечатку комментариями.
.method public hidebysig static void Main() cil managed
{
.entrypoint
// Размер кода 45 (0x2d)
.maxstack 3
.locals init ([0]int32 v,
[1] object o)
// Загружаем 5 в v.
IL_0000: ldc.i4.5
IL_0001: stloc.0
// Упакуем v и сохраняем указатель в o.
IL_0002: ldloc.0
161
Упаковка.и.распаковка.значимых.типов
IL_0003: box [mscorlib]System.Int32
IL_0008: stloc.1
// Загружаем 123 в v.
IL_0009: ldc.i4.s 123
IL_000b: stloc.0
// Упакуем v и сохраняем в стеке указатель для Concat
IL_000c: ldloc.0
IL_000d: box [mscorlib]System.Int32
// Загружаем строку в стек для Concat
IL_0012: ldstr ", "
// Распакуем o: берем указатель в поле Int32 в стеке
IL_0017: ldloc.1
IL_0018: unbox.any [mscorlib]System.Int32
// Упакуем Int32 и сохраняем в стеке указатель для Concat
IL_001d: box [mscorlib]System.Int32
// Вызываем Concat
IL_0022: call string [mscorlib]System.String::Concat(object,
object,
object)
// Строку, возвращенную из Concat, передаем в WriteLine
IL_0027: call void [mscorlib]System.Console::WriteLine(string)
// Метод Main возвращает управление, и приложение завершается
IL_002c: ret
} // Конец метода App::Main
Вначале в стеке создается экземпляр
v
неупакованного значимого типа
Int32
,
которому присваивается число 5. Затем создается переменная
o
типа
Object
, кото-
рая инициализируется указателем на
v
. Однако поскольку ссылочные типы всегда
должны указывать на объекты в куче, C# генерирует соответствующий IL-код
для упаковки
v
и заносит адрес упакованной «копии»
v
в
o
. Теперь величина 123
помещается в неупакованный значимый тип
v
, но это не влияет на упакованное
значение типа
Int32
, которое остается равным 5.
Дальше вызывается метод
WriteLine
, которому нужно передать объект
String
,
но такого объекта нет. Вместо строкового объекта мы имеем неупакованный эк-
земпляр значимого типа
Int32
(
v
), объект
String
(ссылочного типа) и ссылку на
упакованный экземпляр значимого типа
Int32
(
o
), который приводится к неупа-
кованному типу
Int32
. Эти элементы нужно как-то объединить, чтобы получился
объект
String
.
Чтобы создать
String
, компилятор C# формирует код, в котором вызывается
статический метод
Concat
объекта
String
. Есть несколько перегруженных версий
этого метода, различающихся лишь количеством параметров. Поскольку строка
162
Do'stlaringiz bilan baham: |