Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

full sharp / 7Значимые типы

.docx
Скачиваний:
11
Добавлен:
08.03.2015
Размер:
17.98 Кб
Скачать

Значимые типы

Типы значений, или «облегченные» объекты

В C# существуют две основные категории типов: ссылочные (reference types) («тяжеловесные» объекты) и типы значений, или значимые типы (value types) («облегченные» объекты).

Память под ссылочные типы всегда выделяется из кучи (единственное исключение — использование ключевого слова stackalloc); они создают дополнительный уровень абстракции, т. е. требуют доступа через ссылку на место их хранения. Поскольку к этим типам нельзя обращаться напрямую, переменная ссылочного типа всегда хранит ссылку на реальный объект (или null), а не сам объект. А так как память под них выделяется из кучи, исполняющая среда должна быть уверена в правильности каждого запроса на выделение. Рассмотрим следующий код, который может привести к успешному выделению памяти:

Листинг 2.

C#

Matrix m = new Matrix(100, 100);

За кулисами диспетчер памяти CLR получает запрос на выделение, вычисляет необходимый объем памяти для хранения объекта с его заголовком и переменных класса. Затем диспетчер памяти проверяет, достаточно ли свободного места в куче. Если да, объект успешно создается и возвращается ссылка на место его хранения. Если памяти для хранения объекта недостаточно, запускается сборщик мусора для освобождения места и сжатия кучи.

Если процесс прошел успешно, диспетчер памяти должен предпринять еще одно важное действие, прежде чем объект будет записан в память. Оно необходимо для поддержки сбора мусора по поколениям (generational garbage collection) и заключается в том, что генерируется блок кода, называемый барьером записи (write barrier). (Детали реализации сбора мусора по поколениям выходят за рамки этой статьи.) В свою очередь исполняющая среда генерирует барьер записи всякий раз, когда объект записывается по определенному адресу в памяти или когда объект ссылается на другой объект в памяти (например, объект более старшего поколения указывает на объект младшего поколения). Одна из многих деталей, о которых следует помнить, чтобы не нарушить сбор мусора по поколениям, — наличие таких записей у объектов, чтобы они не были ошибочно собраны как мусор в тот момент, когда на них ссылается какой-то объект другого поколения. Как вы, наверное, догадались, барьеры записи создают небольшие издержки в период выполнения, поэтому создание миллионов объектов — не лучший вариант для научных приложений.

Значимые типы хранятся непосредственно в стеке (хотя есть исключение, о котором я вскоре расскажу). Таким типам не требуется дополнительный уровень абстракции и поэтому переменные значимых типов всегда хранят само значение, а не ссылки на другие типы (и поэтому не могут содержать null). Главное преимущество значимых типов по сравнению со ссылочными в том, что создание их экземпляров не приводит к большим издержкам. Память под них выделяется в стеке простым приращением указателя стека, и они не управляются диспетчером памяти. Эти объекты никогда не инициируют сбор мусора. Более того, для значимых типов барьер записи не генерируется.

Примеры значимых типов в C# — элементарные типы данных (int, float, single, byte), перечислимые и структуры. Кстати, сказав ранее, что значимые типы хранятся прямо в стеке, я не употребил слово «всегда», как для ссылочных типов. Тип значения, размещенный внутри ссылочного типа, будет храниться не в стеке, а в куче. Например, взгляните на такой фрагмент кода:

Листинг 3.

C#

class Point

{

private double x, y;

public Point (double x, double y)

{

this.x = x; this.y = y;

}

}

Экземпляр этого класса займет 24 байта, из которых 8 байтов пойдет на заголовок объекта, а оставшиеся 16 — на две переменных типа double: x и y. В то же время размещение ссылочного типа внутри объекта значимого типа (например массива внутри структуры) не приведет к размещению всего объекта в куче. В куче будет выделен только массив, а ссылка на него размещена в структуре в стеке.

Значимые типы наследуют от System.ValueType, который в свою очередь наследует от System.Object. Поэтому значимые типы поддерживают функциональность, подобную той, которая есть у классов. У них могут быть конструкторы (кроме параметризованных), методы, индексаторы и перегруженные операторы; они также могут реализовать интерфейсы. Однако от них нельзя наследовать, и сами они не способны наследовать от других типов. Эти объекты легко поддаются множеству JIT оптимизаций, что приводит к созданию эффективного высокопроизводительного кода.

Загвоздка в том, что можно случайно внести значимые типы в объект и они окажутся в куче, — этот процесс известен как упаковка (boxing). Убедитесь, что значения в вашем коде не упаковываются без необходимости, иначе вы потеряете изначально достигнутое быстродействие. Учтите также, что массивы значимых типов (например массивы типа double или int) хранятся в куче, а не в стеке. В стеке хранится лишь значение, содержащее ссылку на такой массив. Причина в том, что все типы массивов неявно наследуют от System.Array и являются ссылочными типами.

Для научных приложений значимые типы быстрее, эффективнее и предпочтительнее ссылочных типов. В следующем разделе я обращусь к одному из многих возможных применений пользовательских «облегченных» типов данных в научном программировании.

Соседние файлы в папке full sharp