Глава.5 .Примитивные,.ссылочные.и.значимые.типы
Сколько операций упаковки вы насчитали в этом коде? Правильно — одну.
Дело в том, что в классе
System.Console
описан метод
WriteLine
, принимающий
в качестве параметра тип
Int32
:
public static void WriteLine(Int32 value);
В показанных ранее вызовах
WriteLine
переменная
v
, имеющая неупакованный
значимый тип
Int32
, передается по значению. Возможно, где-то у себя
WriteLine
упакует это значение
Int32
, но тут уж ничего не поделаешь. Главное — мы сделали
то, что от нас зависело: убрали упаковку из своего кода.
Пристально взглянув на FCL, можно заметить, что многие перегруженные ме-
тоды используют в качестве параметров значимые типы. Так, тип
System.Console
предлагает несколько перегруженных вариантов метода
WriteLine
:
public static void WriteLine(Boolean);
public static void WriteLine(Char);
public static void WriteLine(Char[]);
public static void WriteLine(Int32);
public static void WriteLine(UInt32);
public static void WriteLine(Int64);
public static void WriteLine(UInt64);
public static void WriteLine(Single);
public static void WriteLine(Double);
public static void WriteLine(Decimal);
public static void WriteLine(Object);
public static void WriteLine(String);
Аналогичный набор перегруженных версий есть у метода
Write
типа
System.
Console
, у метода
Write
типа
System.IO.BinaryWriter
, у методов
Write
и
Write-
Line
типа
System.IO.TextWriter
, у метода
AddValue
типа
System.Runtime.Se-
rialization.SerializationInfo
, у методов
Append
и
Insert
типа
System.Text.
StringBuilder
и т. д. Большинство этих методов имеет перегруженные версии
только затем, чтобы уменьшить количество операций упаковки для наиболее часто
используемых значимых типов.
Если вы определите собственный значимый тип, у этих FCL-классов не будет
соответствующей перегруженной версии для вашего типа. Более того, для ряда
значимых типов, уже существующих в FCL, нет перегруженных версий указан-
ных методов. Если вызывать метод, у которого нет перегруженной версии для
передаваемого значимого типа, результат в конечном итоге будет один — вызов
перегруженного метода, принимающего
Object
. Передача значимого типа как
Object
приведет к упаковке, что отрицательно скажется на производительности.
Определяя собственный класс, можно задать в нем обобщенные методы (возможно,
содержащие параметры типа, которые являются значимыми типами). Обобщения
позволяют определить метод, принимающий любой значимый тип, не требуя при
этом упаковки (см. главу 12).
И последнее, что касается упаковки: если вы знаете, что ваш код будет периоди-
чески заставлять компилятор упаковывать какой-то значимый тип, можно умень-
165
Упаковка.и.распаковка.значимых.типов
шить объем и повысить быстродействие своего кода, выполнив упаковку этого типа
вручную. Взгляните на следующий пример.
using System;
public sealed class Program {
public static void Main() {
Int32 v = 5; // Создаем переменную упакованного значимого типа
#if INEFFICIENT
// При компиляции следующей строки v упакуется
// три раза, расходуя и время, и память
Console.WriteLine("{0}, {1}, {2}", v, v, v);
#else
// Следующие строки дают тот же результат,
// но выполняются намного быстрее и расходуют меньше памяти
Object o = v; // Упакуем вручную v (только единожды)
// При компиляции следующей строки код упаковки не создается
Console.WriteLine("{0}, {1}, {2}", o, o, o);
#endif
}
}
Если компилировать этот код с определенным символическим именем
INEFFICIENT
, компилятор создаст код, трижды выполняющий упаковку
v
и вы-
деляющий память в куче для трех объектов! Это особенно расточительно, так как
каждый объект будет содержать одно значение — 5. Если же компилировать код без
определения символа
INEFFICIENT
, значение
v
будет упаковано только раз и только
один объект будет размещен в куче. Затем при обращении к
Console.WriteLine
трижды передается ссылка на один и тот же упакованный объект. Второй вариант
выполняется
намного
быстрее и расходует меньше памяти в куче.
В этих примерах довольно легко определить, где нужно упаковать экземпляр
значимого типа. Простое правило: если нужна ссылка на экземпляр значимого типа,
этот экземпляр должен быть упакован. Обычно упаковка выполняется, когда надо
передать значимый тип методу, требующему ссылочный тип. Однако могут быть
и другие ситуации, когда требуется упаковать экземпляр значимого типа.
Помните, мы говорили, что неупакованные значимые типы «легче» ссылочных,
поскольку:
память в управляемой куче им не выделяется;
у них нет дополнительных членов, присущих каждому объекту в куче: указателя
на типовой объект и индекса блока синхронизации.
Поскольку неупакованные значимые типы не имеют индекса блока синхрони-
зации, то не может быть и нескольких потоков, синхронизирующих свой доступ
к экземпляру через методы типа
System.Threading.Monitor
(или инструкция
lock
языка C#).
166
Do'stlaringiz bilan baham: |