実行時のメタデータの使用
メタデータと、共通言語ランタイムでのメタデータの役割をより深く理解するために、簡単なプログラムを作成し、メタデータがその有効期間中にどのように機能するかを示します。 MyApp と呼ばれるクラス内の 2 つのメソッドを表すコードの例を次に示します。 Main メソッドは、プログラムのエントリ ポイントです。Add メソッドは、2 つの整数引数の合計値を返します。
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);
}
}
このコードを実行すると、ランタイムがモジュールをメモリに読み込み、このクラスのメタデータを調べます。 モジュールを読み込んだ後、ランタイムはメソッドの Microsoft Intermediate Language (MSIL) ストリームを詳細に分析して、そのストリームを高速のネイティブ機械語命令に変換します。 ランタイムはジャスト イン タイム (JIT) コンパイラを使用して、それぞれのメソッドを必要なときに 1 つずつ、MSIL 命令からネイティブ マシン語コードに変換します。
上記のコードの Main 関数によって生成された MSIL の一部の例を次に示します。 MSIL とメタデータは、MSIL 逆アセンブラー (Ildasm.exe) を使用することにより、任意の .NET Framework アプリケーションから表示できます。
.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 テーブルの 3 行目を調べます。
Add メソッドが記述されているメタデータ トークンによって参照される、MethodDef テーブルの一部の例を次の表に示します。 このアセンブリにはほかにもメタデータ テーブルが存在し、それぞれ独自の値を持っていますが、ここでは、このテーブルだけを参照します。
行 |
Relative Virtual Address (RVA) |
ImplFlags |
Flags |
名前 (文字列ヒープを指す) |
Signature (BLOB ヒープを指す) |
---|---|---|---|---|---|
1 |
0x00002050 |
IL マネージ |
Public ReuseSlot SpecialName RTSpecialName .ctor |
.ctor (コンストラクター) |
|
2 |
0x00002058 |
IL マネージ |
Public Static ReuseSlot |
Main |
String |
3 |
0x0000208c |
IL マネージ |
Public Static ReuseSlot |
Add |
int, int, int |
このテーブルの各列には、コードについての重要な情報が格納されています。 ランタイムは RVA 列を使用して、このメソッドを定義する MSIL の開始メモリ アドレスを計算します。 ImplFlags および Flags 列には、このメソッドを記述するビットマスク (たとえば、メソッドがパブリックかプライベートかを記述するビットマスク) が格納されています。 Name 列は、文字列ヒープからメソッドの名前へのインデックスとなります。 Signature 列は、BLOB ヒープ内のメソッドのシグネチャ定義へのインデックスとなります。
ランタイムは 3 行目の RVA 列から必要なオフセット アドレスを計算し、計算したオフセット アドレスを JIT コンパイラに返します。JIT コンパイラは計算された新しいアドレスに移動します。 JIT コンパイラは、新しいアドレスから、別のメタデータ トークンを検出するまで MSIL の処理を続行し、処理を繰り返します。
ランタイムは、メタデータを使用することにより、コードを読み込み、ネイティブ機械語命令に変換するのに必要なすべての情報にアクセスできます。 このように、メタデータによって自己言及的なファイルが使用可能になり、メタデータと共通型システムを組み合わせて使用することで、言語間で継承が可能になります。