Рабочим названием платформы .NET было |
Введение в архитектуру Microsoft .NET Framework
Ядро системы типов .NET
Общая система типов достаточно обширна, так как при ее проектировании учитывалась необходимость поддержки различных языков программирования и, кроме того, уделялось большое внимание эффективности выполнения программ в среде .NET. Поэтому, для ясности изложения материала мы сначала рассмотрим основное подмножество общей системы типов. Будем называть это подмножество ядром системы типов.
Схема ядра общей системы типов .NET приведена на рис. 1.5. На схеме все типы делятся на две категории: это типы-значения (value types) и ссылочные типы (reference types). Для того чтобы понять причину такого разделения, приведем следующую аналогию. В некоторых языках программирования используются два способа передачи параметров при вызове функции: передача параметра по значению и передача параметров по ссылке. Передача параметра по значению (by value) подразумевает копирование значения параметра, а при передаче параметра по ссылке (by reference) копирования не происходит (вместо этого вызываемая функция получает адрес параметра). Обобщив эту аналогию, мы получим основное отличие типов-значений от ссылочных типов, а именно: использование типов-значений всегда связано с копированием их значений, а работа со ссылочными типами всегда осуществляется через адреса их значений.
Типы-значения представляют собой примитивные типы данных (целые числа и числа с плавающей запятой). Существуют еще пользовательские типы-значения, но мы обсудим их позже.
Ссылочные типы описывают так называемые объектные ссылки (object references), которые представляют собой адреса объектов.
Значения любого типа хранятся в ячейках (location). В качестве ячеек могут выступать локальные и глобальные переменные, параметры методов, поля объектов и элементы массивов. Для каждой ячейки известен тип значений, которые она может содержать.
Особо важным является то обстоятельство, что ячейки не могут содержать объекты. Все объекты размещаются в специальной области памяти, называемой кучей (heap). Таким образом, в ячейках могут храниться только значения типов-значений или объектные ссылки.
Встроенные типы-значения
Встроенные типы-значения делятся на две группы: целые типы и типы с плавающей запятой. Они перечислены в таблицах 1.1 и 1.2, соответственно. В первом столбце каждой из таблиц приведены имена типов, используемые в текстовом представлении CIL (в программах, компилируемых ассемблером ILASM). Во втором столбце перечислены имена, используемые для тех же самых типов в библиотеке классов .NET. В третьем столбце находится краткое описание, в котором указывается знаковость и разрядность типа.
Тип | Имя в .NET Framework Class Library | Описание |
---|---|---|
float32 | System.Single | вещественное (32 бит) |
float64 | System.Double | вещественное (64 бит) |
Для встроенных типов-значений определены правила преобразования значений одного типа в другой тип. Такие преобразования бывают сужающие (narrowing) и расширяющие (widening). При сужающих преобразованиях значение с большей разрядностью переводится в значение с меньшей разрядностью, что может приводить к потере значащих битов. Расширяющие преобразования никогда не приводят к такой потере.
Самоописывающие ссылочные типы
В некоторых объектно-ориентированных языках программирования (например, в C++) объекты могут храниться как в куче (в динамической памяти), так и в переменных: глобальных (в статической памяти) и локальных (на стеке). Поэтому системы типов в таких языках содержат отдельные типы для самого объекта и для объектной ссылки (указателя на объект).
В среде .NET объекты и объектные ссылки хранятся раздельно, а именно: объекты хранятся в куче, а ссылки - в ячейках. Поэтому общая система типов спроектирована таким образом, что один и тот же ссылочный тип может являться как типом объекта, так и типом объектной ссылки.
Каждый объект в куче содержит информацию о своем типе. Поэтому ссылочные типы, представляющие объекты, называются самоописывающими (self-describing).
Два самоописывающих типа являются встроенными - это System.Object (или просто object в текстовом представлении CIL) и System.String (или string ). Тип System.Object является общим базовым классом, от которого непосредственно или транзитивно наследует любой другой класс. Тип System.String используется для представления строковых данных в формате Unicode.
Основу самоописывающих типов составляют классы. Классы могут агрегировать значения других типов, а также наследоваться друг от друга (в .NET поддерживается только одиночное наследование). Классы могут содержать следующие элементы:
- Поля (fields).
Поля являются ячейками, в которых хранятся значения других типов.
- Методы (methods).
Методы представляют собой функции классов. Они бывают статическими (static method) и объектными (instance method). Вызываемый объектный метод всегда получает ссылку на объект, для которого он вызывается. Объектные методы делятся на виртуальные и невиртуальные.
- Свойства (properties).
Свойство представляет собой пару методов, один из которых возвращает некоторое значение, а другой устанавливает это значение.
- События (events).
События используются для асинхронного внесения изменений в объект.
Типы-массивы также относятся к самоописывающим типам, то есть каждый массив представляет собой объект в куче, доступ к которому осуществляется через объектную ссылку. Хотя, строго говоря, типы-массивы не являются классами, считается, что все они наследуют от библиотечного класса System.Array. Типы-массивы интересны тем, что, в отличие от классов, определяемых программистом самостоятельно, они формируются системой автоматически. То есть если мы имеем некоторый тип X, то тип массива, состоящего из элементов типа X, нам уже объявлять не нужно - об этом позаботится система выполнения.
Массивы бывают как одномерными (в этом случае они обрабатываются особенно эффективно благодаря использованию специальных инструкций CIL), так и многомерными. Кроме того, система поддерживает массивы, нижняя граница которых отлична от нуля.
Особого внимания заслуживают особенности представления массивов объектов и массивов типов-значений. Дело в том, что так как объекты не могут храниться в ячейках, мы вынуждены вместо массивов объектов использовать массивы объектных ссылок (см. рис. 1.6), в то время как значения типов-значений хранятся прямо в элементах массива.
Типы-интерфейсы
Интерфейсы служат для компенсации отсутствия в .NET множественного наследования. Они могут рассматриваться как чисто абстрактные классы, содержащие только перечисленные ниже элементы:
- Абстрактные методы.
- Статические методы.
- Статические поля.
- Абстрактные свойства.
- Абстрактные события.
Хотя любой класс может наследоваться только от одного базового класса, он может реализовывать произвольное количество интерфейсов. То есть, интерфейс определяет контракт, которому должен удовлетворять любой класс, реализующий этот интерфейс.
Нужно отметить, что интерфейс может содержать реализации статических методов, но все остальные методы, включая методы свойств и событий, должны оставаться абстрактными.
Совместимость ячеек по присваиванию
Значение может иметь сразу несколько типов. Например, если некоторый объект реализует несколько интерфейсов, то каждый из них является его типом. Та же ситуация наблюдается при наследовании: если класс имеет несколько суперклассов, то объект такого класса может выступать в качестве любого из этих суперклассов.
Как уже говорилось ранее, ячейки, в которых могут храниться значения, также имеют тип. Соответственно, ячейка может содержать только значение, совместимое с ее типом. То есть, общая система типов не допускает присваивание ячейке несовместимого с ее типом значения.
Формулируется следующее условие совместимости значения и ячейки: для того чтобы некоторое значение можно было присвоить заданной ячейке, необходимо и достаточно, чтобы хотя бы один из типов значения совпадал с типом ячейки.
Идентичность и равенство значений
Для объектных ссылок и значений типов-значений вводятся отношения идентичности (identity) и равенства (equality). Эти отношения являются отношениями эквивалентности, то есть они рефлексивны, симметричны и транзитивны.
Отношение идентичности для объектных ссылок вводится следующим образом: две объектных ссылки идентичны тогда и только тогда, когда они содержат адреса одного и того же объекта. На рис. 1.7 изображены три объектных ссылки A, B и C, а также два равных объекта-строки. Так как ссылки A и B содержат адрес одного и того же объекта, то они идентичны между собой, но при этом они не идентичны ссылке C, содержащей адрес другого объекта.
Отношение равенства для объектных ссылок формулируется так: две объектных ссылки равны тогда и только тогда, когда они содержат адреса равных объектов. Все три объектные ссылки, изображенные на рис. 1.7, равны между собой.
Отношение идентичности для типов-значений определяется следующим образом: два значения идентичны тогда и только тогда, когда они принадлежат одному и тому же типу-значению, и представляющие их последовательности битов равны.
Отношение равенства для примитивных типов-значений совпадает с отношением идентичности.
Отношения идентичности и равенства играют большую роль при программировании в среде .NET. Любой объект имеет виртуальный метод Equals, унаследованный от System.Object и выполняющий сравнение объектов на равенство. В зависимости от реализации этого метода, отношение равенства может существенно меняться. При переопределении метода Equals нужно иметь в виду, что два идентичных объекта обязательно должны быть равны.