Глава.5 .Примитивные,.ссылочные.и.значимые.типы
формируется путем конкатенации трех элементов, компилятор выбирает следую-
щую версию метода
Concat
:
public static String Concat(Object arg0, Object arg1, Object arg2);
В качестве первого параметра,
arg0
, передается
v
. Но
v
— это неупакованное
значение, а
arg0
— это значение
Object
, поэтому экземпляр
v
нужно упаковать,
а его адрес передать в качестве
arg0
. Параметром
arg1
является строка
","
в виде
ссылки на объект
String
. И наконец, чтобы передать параметр
arg2
,
o
(ссылка на
Object
) приводится к типу
Int32
. Для этого нужна распаковка (но без копирова-
ния), при которой извлекается адрес неупакованного экземпляра
Int32
внутри
упакованного экземпляра
Int32
. Этот неупакованный экземпляр
Int32
надо опять
упаковать, а его адрес в памяти передать в качестве параметра
arg2
методу
Concat
.
Метод
Concat
вызывает методы
ToString
для каждого указанного объекта и вы-
полняет конкатенацию строковых представлений этих объектов. Возвращаемый
из
Concat
объект
String
передается затем методу
WriteLine
, который отображает
окончательный результат.
Полученный IL-код станет эффективнее, если обращение к
WriteLine
пере-
писать:
Console.WriteLine(v + ", " + o); // Отображается "123, 5"
Этот вариант строки отличается от предыдущего только отсутствием для пере-
менной
o
операции приведения типа
(Int32)
. Этот код выполняется быстрее, так
как
o
уже является ссылочным типом
Object
и его адрес можно сразу передать
методу
Concat
. Отказавшись от приведения типа, я избавился от двух операций:
распаковки и упаковки. В этом легко убедиться, если заново собрать приложение
и посмотреть на сгенерированный IL-код:
.method public hidebysig static void Main() cil managed
{
.entrypoint
// Размер кода 35 (0x23)
.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
IL_0003: box [mscorlib]System.Int32
IL_0008: stloc.1
// Загружаем 123 в v
IL_0009: ldc.i4.s 123
IL_000b: stloc.0
163
Упаковка.и.распаковка.значимых.типов
// Упакуем v и сохраняем в стеке указатель для Concat
IL_000c: ldloc.0
IL_000d: box [mscorlib]System.Int32
// Загружаем строку в стек для Concat
IL_0012: ldstr ", "
// Загружаем в стек адрес упакованного Int32 для Concat
IL_0017: ldloc.1
// Вызываем Concat
IL_0018: call string [mscorlib]System.String::Concat(object,
object,
object)
// Строку, возвращенную из Concat, передаем в WriteLine
IL_001d: call void [mscorlib]System.Console::WriteLine(string)
// Main возвращает управление, чем завершается работа приложения
IL_0022: ret
} // Конец метода App::Main
Беглое сравнение двух версий IL-кода метода
Main
показывает, что вариант без
приведения типа
Int32
на 10 байт меньше, чем вариант с приведением типа. До-
полнительные операции распаковки/упаковки, безусловно, приводят к разрастанию
кода. Если мы пойдем дальше, то увидим, что эти операции потребуют выделения
памяти в управляемой куче для дополнительного объекта, которую в будущем дол-
жен освободить уборщик мусора. Конечно, обе версии приводят к одному результату
и разница в скорости незаметна, однако лишние операции упаковки, выполняемые
многократно (например, в цикле), могут заметно повлиять на производительность
приложения и расходование памяти.
Предыдущий код можно улучшить, изменив вызов метода
WriteLine
:
Console.WriteLine(v.ToString() + ", " + o); // Отображается "123, 5"
Для неупакованного значимого типа
v
теперь вызывается метод
ToString
, воз-
вращающий
String
. Строковые объекты являются ссылочными типами и могут
легко передаваться в метод
Concat
без упаковки.
Вот еще один пример, демонстрирующий упаковку и распаковку:
public static void Main() {
Int32 v = 5; // Создаем неупакованную переменную значимого типа
Object o = v; // o указывает на упакованную версию v
v = 123; // Изменяет неупакованный значимый тип на 123
Console.WriteLine(v); // Отображает "123"
v = (Int32) o; // Распаковывает и копирует o в v
Console.WriteLine(v); // Отображает "5"
}
164
Do'stlaringiz bilan baham: |