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 中间语言 (MSIL) 流执行广泛的分析,将其转换为快速本机指令。运行库根据需要使用实时 (JIT) 编译器将 MSIL 指令转换为本机代码,每次转换一个方法。
下面的示例显示了从以前代码的 Main 功能生成的部分 MSIL。您可以使用 MSIL 反汇编程序 (Ildasm.exe) 从任何 .NET Framework 应用程序中查看 MSIL 和元数据。
.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 表的第三行。
下表显示了说明 Add 方法的元数据标记所引用的 MethodDef 表的一部分。虽然程序集中存在其他元数据表并具有它们自己唯一的值,但这里只讨论该表。
Row
相对虚拟地址 (RVA)
ImplFlags
Flags
Name
(指向字符串堆。)
Signature(指向 Blob 堆)
1
0x00002050
IL
Managed
Public
ReuseSlot
SpecialName
RTSpecialName
.ctor
.ctor(构造函数)
2
0x00002058
IL
Managed
Public
Static
ReuseSlot
Main
String
3
0x0000208c
IL
Managed
Public
Static
ReuseSlot
Add
int, int, int
该表的每一列都包含有关代码的重要信息。RVA 列允许运行库计算定义该方法的 MSIL 的起始内存地址。ImplFlags 和 Flags 列包含说明该方法的位屏蔽(例如,该方法是公共的还是私有的)。Name 列对来自字符串堆的方法的名称进行了索引。Signature 列对在 Blob 堆中的方法签名的定义进行了索引。
运行库在第三行的 RVA 列计算所需的偏移量地址并将该地址返回到 JIT 编译器,然后,JIT 编译器进入新地址。JIT 编译器继续在新地址处理 MSIL,直到它遇到另一个元数据标记,之后,重复该过程。
使用元数据,运行库可以访问加载代码并将其处理为本机指令所需的所有信息。以这种方式,元数据使自描述文件、公共类型系统和跨语言继承成为可能。