Рабочим названием платформы .NET было |
Формат метаданных. Взаимодействие программных компонентов
Взаимодействие компонентов в среде .NET
Организация компонентного программирования на платформе .NET необычайно проста и удобна для программиста. Давайте обсудим, какие особенности .NET обеспечивают это удобство.
Во-первых, компилятор любого языка программирования, реализованного на платформе .NET, генерирует сборки, содержащие CIL-код и метаданные. При этом формат этих сборок определяется спецификацией CLI и не зависит от языка программирования.
Во-вторых, любая программа, работающая в среде .NET, использует общую систему типов CTS. Поэтому представление данных в памяти определяется CTS и не зависит от языка программирования, на котором написана программа.
В-третьих, выполнение любой программы управляется средой выполнения CLR. Это означает, что среда выполнения контролирует JIT-компиляцию CIL-кода программы, выполняет управление памятью и в каждый момент времени имеет всю информацию о состоянии программы.
Эти три особенности платформы .NET позволяют среде выполнения автоматически обеспечивать взаимодействие компонентов вне зависимости от того, на каком языке они написаны.
На рис. 2.12 изображена схема взаимодействия двух объектов на платформе .NET. Объекты Client и Server находятся в разных компонентах, работающих в адресном пространстве одного процесса (здесь мы не рассматриваем межпроцессное взаимодействие, а также взаимодействие по сети). Передача сообщения от объекта Client объекту Server сводится к простому вызову соответствующего метода объекта Server, то есть в среде .NET не нужны никакие объекты-заглушки.
Компоненты на платформе .NET представляют собой сборки .NET. Сборка .NET может статически импортировать любую другую сборку и свободно использовать типы, экспортируемые из этой сборки. Для этого информация об импортируемой сборке заносится в таблицу метаданных AssemblyRef, информация о каждом импортируемом типе - в таблицу TypeRef, а информация о каждом импортируемом методе и поле - в таблицу MemberRef. Кроме того, сборка может импортироваться динамически через рефлексию (мы рассмотрим эту возможность в следующей лекции).
Видимость и контроль доступа
Метаданные в сборках .NET содержат полную информацию о типах. При этом каждый тип может экспортироваться или не экспортироваться из сборки, а каждый член типа (метод, поле, свойство и т.д.) должен быть объявлен с определенным значением флага доступа.
Видимость типа (т.е. экспортируется он из сборки или нет) определяется флагом видимости и хранится в поле Flags соответствующей этому типу записи в таблице метаданных TypeDef. В таблице 2.3 приведен набор флагов видимости для типов.
Доступ к методам, полям и свойствам типа определяется значением флага доступа. Описание допустимых значений приводится в таблице 2.4.
Флаг | Значение | Описание |
---|---|---|
CompilerControlled | 0x00000000 | Доступ контролируется компилятором |
Private | 0x00000001 | Доступен только внутри типа |
FamAndAssem | 0x00000002 | Доступен наследникам типа, объявленным внутри сборки |
Assembly | 0x00000003 | Доступен только внутри сборки |
Family | 0x00000004 | Доступен наследникам типа |
FamOrAssem | 0x00000005 | Доступен внутри сборки, а также наследникам типа |
Public | 0x00000006 | Доступен везде |
Пример межъязыкового взаимодействия
Рассмотрим учебный пример, который представляет собой компонентную систему, написанную сразу на четырех языках. Диаграмма классов примера дана на рисунке 2.13.
Абстрактный класс SortedArray реализован на Visual Basic .NET. В этом классе определено поле Arr, представляющее собой массив целых чисел. Конструктор класса SortedArray копирует в это поле массив, передаваемый ему в качестве параметра, а затем вызывает абстрактный метод Sort() для сортировки этого массива. Для доступа к отсортированному массиву используются свойства Array и Count:
Public MustInherit Class SortedArray Protected Arr() As Integer Protected MustOverride Sub Sort() Public Sub New(ByVal A() As Integer) Dim i As Integer Arr = New Integer(A.Length - 1) {} For i = 0 To A.Length - 1 Arr(i) = A(i) Next Sort() End Sub Default Public ReadOnly Property Array (ByVal Index As Integer) As Integer Get Return Arr(Index) End Get End Property Public ReadOnly Property Count() As Integer Get Return Arr.Length End Get End Property End Class
Класс BubbleSortedArray написан на Visual C++ with Managed Extensions. Он переопределяет абстрактный метод Sort(), реализуя в нем пузырьковую сортировку:
using namespace VBLib; public _ _gc class BubbleSortedArray: public SortedArray { protected: void Sort() { for (int i = Arr->Length, flag = 1; i > 1 && flag; i--) { flag = 0; for (int j = 0; j < i-1; j++) if (Arr[j] < Arr[j+1]) { int tmp = Arr[j]; Arr[j] = Arr[j+1]; Arr[j+1] = tmp; flag = 1; } } } public: BubbleSortedArray(int A _ _gc []): SortedArray(A) { } };
Класс InsertSortedArray написан на Visual C#. Он переопределяет абстрактный метод Sort(), реализуя в нем сортировку вставками.
using VBLib; public class InsertSortedArray: SortedArray { protected override void Sort() { for (int i = 0; i < Arr.Length-1; i++) { int max = i; for (int j = i+1; j < Arr.Length; j++) if (Arr[j] > Arr[max]) max = j; int tmp = Arr[i]; Arr[i] = Arr[max]; Arr[max] = tmp; } } public InsertSortedArray(int[] A): base(A) { } }
И, наконец, все вышеперечисленные классы используются в программе, написанной на Visual J#.
package JsApp; import VBLib.SortedArray; public class Main { public static void main(String[] args) { int A[] = new int[] { 5, 1, 6, 0, -4, 3 }; SortedArray SA1 = new BubbleSortedArray(A), SA2 = new InsertSortedArray(A); for (int i = 0; i < SA1.get_Count(); i++) System.out.print(""+SA1.get_Array(i)+" "); System.out.println(); for (int i = 0; i < SA2.get_Count(); i++) System.out.print(""+SA2.get_Array(i)+" "); System.out.println(); } }
Общая спецификация языков
Различные языки программирования, которые уже реализованы или могут быть реализованы на платформе .NET, используют общую систему типов (CTS) в качестве модели данных. Общая система типов поддерживает все типы, с которыми работают распространенные в настоящее время языки программирования, но не каждый язык поддерживает все типы, реализованные в общей системе типов. Например, Visual Basic допускает типизированные ссылки в качестве параметров методов, и эти типизированные ссылки реализованы в общей системе типов, но C# их не понимает и, соответственно, не может использовать методы с такими параметрами.
Для того, чтобы любую библиотеку классов можно было использовать из любого языка платформы .NET, разработчики .NET придумали общую спецификацию языков (Common Language Specification - CLS).
В этой спецификации оговариваются правила, которым должны следовать разработчики языков и библиотек. То есть она описывает некоторое подмножество общей системы типов, и если некий язык реализует хотя бы это подмножество, а библиотека использует только входящие в это подмножество типы, то такая библиотека может быть использована из этого языка.
В терминологии CLS библиотеки, соответствующие спецификации CLS, называются средами (frameworks), но мы будем называть их CLS-библиотеками. Компиляторы, генерирующие код, из которого можно получить доступ к CLS-библиотекам, называются потребителями (consumers). Компиляторы, которые являются потребителями, но, к тому же, способны создавать новые CLS-библиотеки, называются расширителями (extenders).
Приведем основные правила общей спецификации языков:
- Спецификация распространяется только на доступные извне части экспортируемых из библиотеки типов. То, что остается внутри библиотеки, может использовать любые типы, не оглядываясь на спецификацию.
- Упакованные типы-значения, неуправляемые указатели и типизированные ссылки не должны использоваться. Дело в том, что отнюдь не во всех языках, реализованных на платформе .NET, есть соответствующие понятия, и, более того, добавление этих понятий во все языки представляется нецелесообразным.
- Регистр букв в идентификаторах не имеет значения. Это правило объясняется тем, что в некоторых языках программирования (например, в Паскале) регистр букв не различается.
- Методы не должны иметь переменного количества параметров.
- Глобальные поля и методы не поддерживаются спецификацией.
- Объекты исключений должны наследовать от System.Exception.