Метаданные и компоненты с самоописанием
В прошлом программный компонент (EXE или DLL), написанный на одном языке, не мог просто так использовать программный компонент, написанный на другом языке. Модель COM стала шагом вперед в решении этой проблемы. Платформа .NET упрощает взаимодействие компонентов, позволяя компиляторам добавлять дополнительные описательные данные во все модули и сборки. Эти данные, называемые метаданными, способствуют эффективному взаимодействию компонентов.
Метаданные — это данные в двоичном формате с описанием программы, хранящиеся либо в переносимом исполняемом (PE) файле среды CLR, либо в памяти. При компиляции кода в PE-файл метаданные вставляются в одну часть файла, и код преобразуется в общий промежуточный язык (CIL) и вставляется в другую часть файла. В метаданных описываются все типы и члены, определенные или используемые в модуле или сборке. При исполнении кода среда выполнения загружает метаданные в память и обращается к ним для получения сведений о классах, членах, наследовании и других элементах кода.
В метаданных в независимом от языка виде описываются все типы и члены, определенные в коде. В метаданных хранятся следующие сведения.
Описание сборки.
Удостоверение (имя, версия, язык и региональные параметры, открытый ключ).
Экспортируемые типы.
Другие сборки, от которых зависит данная сборка.
Необходимые разрешения безопасности.
Описание типов.
Имя, видимость, базовый класс и реализованные интерфейсы.
Члены (методы, поля, свойства, события, вложенные типы).
Атрибуты.
- Дополнительные описательные элементы, изменяющие типы и члены.
Преимущества метаданных
Метаданные — это ключ к более простой модели программирования; они устраняют необходимость в файлах IDL, файлах заголовков или каких-либо внешних методах ссылки на компоненты. Метаданные позволяют языкам платформы .NET автоматически описывать себя не зависящим от языка образом незаметно для разработчика и пользователя. К тому же метаданные имеют возможности для расширения за счет использование атрибутов. Метаданные обеспечивают также следующие преимущества.
Файлы с самоописанием.
Модули среды CLR и сборки обладают свойством самоописания. Метаданные модуля содержат все необходимое для взаимодействия с другим модулем. Метаданные автоматически предоставляют функциональные возможности IDL для модели СОМ, в связи с чем один и тот же файл может использоваться и для определения, и для реализации. Модули и сборки среды выполнения даже не требуют регистрации в операционной системе. Благодаря этому описания, используемые средой выполнения, всегда отражают фактический код в скомпилированном файле, что повышает надежность приложения.
Взаимодействие языков и упрощение разработки на основе компонентов
Метаданные содержат полные сведения о скомпилированном коде, необходимые для наследования класса из PE-файла, написанного на другом языке. Вы можете создать экземпляр любого класса, написанного на любом управляемом языке (любой язык, предназначенный для среды CLR), не беспокоясь о явном маршаллинге или использовании пользовательского кода взаимодействия.
Атрибуты.
Платформа .NET позволяет объявлять особые виды метаданных, называемые атрибутами, в скомпилированном файле. Атрибуты находятся в .NET и используются для более детального контроля над работой программы во время ее выполнения. Также с помощью атрибутов в файлы .NET можно вносить пользовательские метаданные, определяемые самим пользователем. Дополнительные сведения см. в разделе Атрибуты.
Метаданные и структура PE-файла
Метаданные хранятся в одном разделе переносимого исполняемого файла .NET (PE), а общий промежуточный язык (CIL) хранится в другом разделе PE-файла. Раздел файла с метаданными содержит ряд табличных структур и структур данных кучи. Часть CIL содержит маркеры CIL и метаданных, ссылающиеся на часть метаданных pe-файла. При использовании таких средств, как il Disassembler (Ildasm.exe) для просмотра CIL кода, могут возникнуть маркеры метаданных.
Таблицы и кучи метаданных
Каждая таблица метаданных содержит сведения об элементах программы. К примеру, в одной таблице метаданных описываются классы в коде, в другой — поля и так далее. Если в коде используется десять классов, таблица классов будет содержать десять строк, по одной для каждого класса. Таблицы метаданных ссылаются на другие таблицы и кучи. Например, таблица метаданных для классов ссылается на таблицу методов.
В метаданных сведения также хранятся в четырех структурах кучи — в куче строк, больших двоичных объектов, пользовательских строк и идентификаторов GUID. Все строки, используемые для названия типов и членов, хранятся в куче строк. Например, в таблице методов не хранится имя конкретного метода, но содержится ссылка на имя этого метода, находящееся в куче строк.
Лексемы метаданных
Каждая строка каждой таблицы метаданных однозначно определяется в части CIL-файла PE маркером метаданных. Маркеры метаданных концептуально похожи на указатели, сохраняемые в CIL, ссылающиеся на определенную таблицу метаданных.
Лексема метаданных является четырехбайтным числом. Старший байт указывает на таблицу метаданных, к которой относится данная лексема (метод, тип и т. д.). Остальные три байта определяют строку в таблице метаданных, которая соответствует описываемому программному элементу. Если определить метод в C# и скомпилировать его в PE-файл, в части CIL файла PE может существовать следующий маркер метаданных:
0x06000004
Старший байт (0x06
) указывает, что это лексема MethodDef. Три младших байта (000004
) отсылают среду CLR к четвертой строке таблицы MethodDef для получения сведений с описанием определения метода.
Метаданные внутри PE-файла
После компиляции программы для среды CLR она преобразуется в PE-файл, состоящий из трех разделов. В следующей таблице описано содержание каждого раздела.
Раздел PE-файла | Содержание раздела PE-файла |
---|---|
Заголовок PE | Индекс основных разделов PE-файла и адрес точки входа. Среда выполнения использует эти сведения для определения файла как PE-файла и определения начала выполнения при загрузке программы в память. |
Инструкции по CIL | Инструкции по языку промежуточного языка (CIL), составляющие код. Многие инструкции CIL сопровождаются маркерами метаданных. |
Метаданные | Таблицы и кучи метаданных Среда выполнения использует этот раздел для записи сведений о каждом типе и члене в коде. Этот раздел также содержит пользовательские атрибуты и сведения о безопасности. |
Использование метаданных во время выполнения
Для лучшего понимания метаданных и их роли в среде CLR полезно написать простую программу и наглядно проиллюстрировать, как метаданные влияют на работу среды выполнения. В приведенном ниже примере показано два метода внутри класса MyApp
. Метод Main
является точкой входа программы, а метод Add
— просто возвращает сумму двух целочисленных аргументов.
Public Class MyApp
Public Shared Sub Main()
Dim ValueOne As Integer = 10
Dim ValueTwo As Integer = 20
Console.WriteLine("The Value is: {0}", Add(ValueOne, ValueTwo))
End Sub
Public Shared Function Add(One As Integer, Two As Integer) As Integer
Return (One + Two)
End Function
End Class
using System;
public class MyApp
{
public static int Main()
{
int ValueOne = 10;
int ValueTwo = 20;
Console.WriteLine("The Value is: {0}", Add(ValueOne, ValueTwo));
return 0;
}
public static int Add(int One, int Two)
{
return (One + Two);
}
}
При запуске кода среда выполнения загружает модуль в память и обращается к метаданным за сведениями об этом классе. После загрузки среда выполнения выполняет обширный анализ потока общего промежуточного языка метода (CIL), чтобы преобразовать его в быстрые инструкции машинного компьютера. Среда выполнения использует JIT-компилятор для преобразования инструкций CIL в машинный код по одному методу по мере необходимости.
В следующем примере показана часть CIL, созданная из функции предыдущего кода Main
. CIL и метаданные можно просмотреть из любого приложения .NET с помощью дизассемблера CIL (Ildasm.exe).
.entrypoint
.maxstack 3
.locals ([0] int32 ValueOne,
[1] int32 ValueTwo,
[2] int32 V_2,
[3] int32 V_3)
IL_0000: ldc.i4.s 10
IL_0002: stloc.0
IL_0003: ldc.i4.s 20
IL_0005: stloc.1
IL_0006: ldstr "The Value is: {0}"
IL_000b: ldloc.0
IL_000c: ldloc.1
IL_000d: call int32 ConsoleApplication.MyApp::Add(int32,int32) /* 06000003 */
Компилятор JIT считывает CIL для всего метода, тщательно анализирует его и создает эффективные собственные инструкции для метода. В строке IL_000d
появляется маркер метаданных для метода Add
(/*
06000003 */
), и среда выполнения использует этот маркер для обращения к третьей строке таблицы MethodDef.
В приведенной ниже таблице показана часть таблицы MethodDef, на которую ссылается лексема метаданных, описывающая метод Add
. В сборке существуют и другие таблицы метаданных со своими уникальными значениями, но в данный момент рассматривается только эта таблица.
Строка | Относительный виртуальный адрес (RVA) | Неявные флаги | Флаги | Имя. (Указывает на кучу строк.) |
Сигнатура (указывает на кучу больших двоичных объектов) |
---|---|---|---|---|---|
1 | 0x00002050 | IL Управляется |
Общедоступный ReuseSlot SpecialName RTSpecialName .ctor |
.ctor (конструктор) | |
2 | 0x00002058 | IL Управляется |
Общедоступный Статические ReuseSlot |
Главная | Строка |
3 | 0x0000208c | IL Управляется |
Общедоступный Статические ReuseSlot |
Добавить | int, int, int |
В каждом столбце таблицы содержатся важные сведения о коде. Столбец RVA позволяет среде выполнения вычислять начальный адрес памяти CIL, который определяет этот метод. Столбцы Неявные флаги и Флаги содержат битовые маски, описывающие метод (например, является ли метод общим или закрытым). Столбец Имя содержит указатель имени метода из кучи строк. Столбец Сигнатура содержит указатель определения сигнатуры метода в куче больших двоичных объектов.
Среда выполнения вычисляет нужный относительный адрес из столбца RVA в третьей строке и возвращает этот адрес JIT-компилятору, который затем переходит к новому адресу. Компилятор JIT продолжает обрабатывать CIL на новом адресе, пока не столкнется с другим маркером метаданных и процесс повторяется.
Благодаря метаданным среда выполнения имеет доступ к любым сведениям, необходимым для загрузки кода пользователя и его преобразования в инструкции машинного кода. Таким образом, метаданные позволяют использовать файлы с самоописанием и — совместно с системой общих типов — межъязыковое наследование.