Использование метаданных во время выполнения
Для лучшего понимания метаданных и их роли в среде 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);
}
}
При запуске кода среда выполнения загружает модуль в память и обращается к метаданным за сведениями об этом классе. После загрузки среда выполнения проводит подробный анализ потока кода MSIL для метода, чтобы преобразовать его в инструкции машинного кода (выполнение которых осуществляется максимально быстро). В среде выполнения для преобразования инструкций MSIL в исходный машинный код используется JIT-компилятор. Преобразование выполняется по одному методу за раз по мере необходимости.
В приведенном ниже примере показана часть кода MSIL, полученного для функции Main из предыдущего кода. Код MSIL и метаданные любого приложения .NET Framework можно просмотреть при помощи дизассемблера MSIL (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-компилятор считывает код MSIL для всего метода, проводит тщательный анализ и создает эффективный набор машинных инструкций для этого метода. В строке IL_000d появляется лексема метаданных для метода Add (/* 06000003 */) и среда выполнения использует эту лексему для обращения к третьей строке таблице MethodDef.
В приведенной ниже таблице показана часть таблицы MethodDef, на которую ссылается лексема метаданных, описывающая метод Add. В сборке существуют и другие таблицы метаданных со своими уникальными значениями, но в данный момент рассматривается только эта таблица.
Строка |
Относительный виртуальный адрес (RVA) |
Неявные флаги |
Флаги |
Имя (Указывает на кучу строк.) |
Сигнатура (указывает на кучу больших двоичных объектов) |
---|---|---|---|---|---|
1 |
0x00002050 |
IL Управляемое |
Открытый ReuseSlot SpecialName RTSpecialName .ctor |
.ctor (конструктор) |
|
2 |
0x00002058 |
IL Управляемое |
Открытый Static ReuseSlot |
Main |
Строка |
3 |
0x0000208c |
IL Управляемое |
Открытый Статический ReuseSlot |
Add |
int, int, int |
В каждом столбце таблицы содержатся важные сведения о коде. Значения в столбце RVA позволяют среде выполнения вычислить начальный адрес в памяти кода MSIL, определяющего этот метод. Столбцы Неявные флаги и Флаги содержат битовые маски, описывающие метод (например, является ли метод общим или закрытым). Столбец Имя содержит указатель имени метода из кучи строк. Столбец Сигнатура содержит указатель определения сигнатуры метода в куче больших двоичных объектов.
Среда выполнения вычисляет нужный относительный адрес из столбца RVA в третьей строке и возвращает этот адрес JIT-компилятору, который затем переходит к новому адресу. JIT-компилятор продолжает обрабатывать код MSIL по новому адресу до тех пор, пока не найдет другую лексему метаданных, и затем процесс повторяется заново.
Благодаря метаданным среда выполнения имеет доступ к любым сведениям, необходимым для загрузки кода пользователя и его преобразования в инструкции машинного кода. Таким образом, метаданные позволяют использовать файлы с самоописанием и — совместно с системой общих типов — межъязыковое наследование.