Run-Time Use of Metadata
To better understand metadata and its role in the common language runtime, it might be helpful to construct a simple program and illustrate how metadata affects its run-time life. The following code example shows two methods inside a class called MyApp
. The Main
method is the program entry point, while the Add
method simply returns the sum of two integer arguments.
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);
}
}
When the code runs, the runtime loads the module into memory and consults the metadata for this class. Once loaded, the runtime performs extensive analysis of the method's Microsoft intermediate language (MSIL) stream to convert it to fast native machine instructions. The runtime uses a just-in-time (JIT) compiler to convert the MSIL instructions to native machine code one method at a time as needed.
The following example shows part of the MSIL produced from the previous code's Main
function. You can view the MSIL and metadata from any .NET Framework application using the MSIL Disassembler (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 */
The JIT compiler reads the MSIL for the whole method, analyzes it thoroughly, and generates efficient native instructions for the method. At IL_000d
, a metadata token for the Add
method (/*
06000003 */
) is encountered and the runtime uses the token to consult the third row of the MethodDef table.
The following table shows part of the MethodDef table referenced by the metadata token that describes the Add
method. While other metadata tables exist in this assembly and have their own unique values, only this table is discussed.
Row | Relative Virtual Address (RVA) | ImplFlags | Flags | Name (Points to string heap.) | Signature (Points to blob heap.) |
---|---|---|---|---|---|
1 |
0x00002050 |
IL Managed |
Public ReuseSlot SpecialName RTSpecialName .ctor |
.ctor (constructor) |
|
2 |
0x00002058 |
IL Managed |
Public Static ReuseSlot |
Main |
String |
3 |
0x0000208c |
IL Managed |
Public Static ReuseSlot |
Add |
int, int, int |
Each column of the table contains important information about your code. The RVA column allows the runtime to calculate the starting memory address of the MSIL that defines this method. The ImplFlags and Flags columns contain bitmasks that describe the method (for example, whether the method is public or private). The Name column indexes the name of the method from the string heap. The Signature column indexes the definition of the method's signature in the blob heap.
The runtime calculates the desired offset address from the RVA column in the third row and returns this address to the JIT compiler, which then proceeds to the new address. The JIT compiler continues to process MSIL at the new address until it encounters another metadata token and the process is repeated.
Using metadata, the runtime has access to all the information it needs to load your code and process it into native machine instructions. In this manner, metadata enables self-describing files and, together with the common type system, cross-language inheritance.
See Also
Concepts
Compiling to MSIL
Compiling MSIL to Native Code