在网上发现了一个非常好的MSIL教程,可惜是英文版的,于是就翻译了一下,与大家共享,
原文http://www.codeguru.com/Csharp/.NET/net_general/il/article.php/c4635,初次翻译文章,请大家指正。
介绍
微软中间语言 (MSIL) 是一种语言,是许多编译器(C#,VB.NET等)的输出. ILDasm (中间语言反汇编器)程序和.Net Framework SDK(FrameworkSDK/Bin/ildasm.exe)打包在一起,让用户以人可阅读的格式查看MSIL代码。通过该工具,我们可以打开任何.net可执行文件(exe或dll)并查看其MSIL代码。
ILAsm 程序(中间语言编译器)从MSIL语言生成可执行文件。我们可以在WINNT / Microsoft.NET /Framework /vn.nn.nn目录中找到这个程序。
许多Visual C++程序员开始.net开发是因为他们对.NET框架的底层发生了一些什么感兴趣。学习MSIL给了用户理解某些对C#程序员或VB.NET程序员来说是透明的东西的机会。通晓MSIL给.NET程序员更多的能力。我们从不需要直接用MSIL编写程序,但是在某些情况下是非常有用的,我们可以用ILDasm打开程序的MSIL代码,查看它到底做了一些什么。
一个Doc格式的MSIL参考对.NET开发人员来说比较有用,它也许可以在Framework SDK目录下找到:
在工作中,我也用到了一个MSDN的ILDAsm教程,一篇2001年5月由John Robbins发表在MSDN杂志的优秀的文章: "ILDASM is Your New Best Friend"。
我想学习一门语言最好的途径就是用它写一些程序,所以我决定写一些小的MSIL程序。实际上,我们有写这些代码——是C#编译器生成的,我只是做一了一些小的更改,并加了许多注释以描述MSIL是如何工作的。
通过阅读附在本文的例子可以帮助.NET程序员理解中间语言,帮助其在需要的时候更易读懂MSIL代码。
一般信息
在MSIL中,所有的操作都在栈上完成。当调用一个函数的时候,其参数和局部变量都被分配到栈上。函数的代码从该栈开始,把一些值压入栈,对这些值进行一些操作,从栈上取出值。
执行MSIL名利和函数由3个步骤完成:
1. 把命令操作数和函数参数压入栈。
2. 执行命令或者调用函数。命令或函数从栈中取出他们的操作数(参数)并把他们压入结果栈 (返回值)。
3. 从栈中读取结果值。
步骤1~3是可选的,例如,void函数不会压入一个结果值到栈。
栈包含值类型对象和引用类型对象的引用。引用类型对象本身保存在堆中。
用来把一个值压入栈中的MSIL命令是ld... (装载),用来从栈中取出值的命令是st... (存储),因为值都存在变量中。我们可以把入栈操作叫做装载,出栈操作叫做存储。
示例项目
本文附上的代码中包含了许多用MSIL写的控制台程序. 如果需要编译他们,请确定ILAsm程序可以通过PATH访问。每个项目都是一个Visual Studio解决方案,IL源文件可以用VS的文本编辑器打开,Build命令运行ILAsm 程序在项目所在目录生成exe文件,run命令执行该文件。在每个程序的末尾,我加了几行代码,他们可以用C#来写:
Console.WriteLine("Press Enter to continue");
Console.Read();
这样,当从Windows Explorer运行的时候,就可以看到程序的输出。
下面是所含项目的列表:
1. 打印字符串—打印字符传到控制台。
2. 赋值—给一个int变量赋值并把它打印到控制台。
3. 运算—从控制台读取2个数字,惊醒+,-和乘的操作,并显示结果。
4. 数组— 分配一个int类型的数组,给他的元素赋值,打印其元素和数组的长度。
5. 比较— 输入2个数字并打印出最小的那个。
6. 数组2— 用循环填充数组元素并打印某些元素。
7. 不安全代码— 使用unsafe指针访问数组元素。
8. PInvoke— 调用Win32 API。
9. 类— 和类一起工作。
10. 异常— 异常处理。
我假设你以在这所说的顺序阅读这些项目。在下面的项目描述中,我用程序来解释每一条MSIL命令,并给出一些代码片段。
打印字符串
PrintString 就是MSIL版的 Hello, World
在代码中用到的MSIL指令如下:
l .entrypoint— 定义程序的入口点(该函数在程序启动的时候由.NET 运行库调用)
l .maxstack— 定义函数代码所用堆栈的最大深度。C#编译器可以对每个函数设置准确的值, 在例子中,我把他设为8。
用到的MSIL命令如下:
调用静态函数很简单。我们把函数的参数压入堆栈,调用函数,然后从堆栈中读取函数的返回值(如果是非void函数)。Console.WriteLine 就是一个这样的函数。
下面是代码:
.assembly PrintString {}
/*
Console.WriteLine("Hello, World)"
*/
.method static public void main() il managed
{
.entrypoint // 该函数是程序的入口
.maxstack 8
// *****************************************************
// Console.WriteLine("Hello, World)";
// *****************************************************
ldstr "Hello, World" // 把字符串压入堆栈
// 调用静态的System.Console.Writeline函数
// (函数移除栈顶的字符串)
call void [mscorlib]System.Console::WriteLine
(class System.String)
// *****************************************************
ldstr "Press Enter to continue"
call void [mscorlib]System.Console::WriteLine
(class System.String)
// 调用 System.Console.Read 函数
call int32 [mscorlib]System.Console::Read()
// pop 指令移除栈顶元素
// (移除由Read()函数返回的数字
pop
// *****************************************************
ret
}
赋值
该程序给一个变量赋与int值并把它打印到控制台窗口。
命令:
代码:
.assembly XequalN {}
// int x;
// x = 7;
// Console.WriteLine(x);
.method static public void main() il managed
{
.entrypoint
.maxstack 8
.locals init ([0] int32 x) // 分配一个局部变量
// *****************************************************
// x = 7;
// *****************************************************
ldc.i4.7 // 把常量装入堆栈
stloc.0 // 把堆栈中的值存入第0个变量
// *****************************************************
// Console.WriteLine(x);
// *****************************************************
ldloc.0 // 把第0个变量转入堆栈
call void [mscorlib]System.Console::WriteLine(int32)
ret
}
数据运算
本程序从控制台读取2个数字,对它们进行简单的运算,然后显示结果。
命令:
代码片段:
.assembly Operations {}
/*
// 程序的C#代码:
int x, y, z;
string s;
Console.WriteLine("Enter x:");
s = Console.ReadLine();
x = Int32.Parse(s);
Console.WriteLine("Enter y:");
s = Console.ReadLine();
y = Int32.Parse(s);
z = x + y;
Console.Write("x + y = ");
Console.Write(z);
Console.WriteLine("");
z = x - y;
Console.Write("x - y = ");
Console.Write(z);
Console.WriteLine("");
z = x * y;
Console.Write("x * y = ");
Console.Write(z);
Console.WriteLine("");
*/
.method static public void main() il managed
{
.entrypoint
.maxstack 8
.locals init ([0] int32 x,
[1] int32 y,
[2] int32 z,
[3] string s)
// *****************************************************
// Console.WriteLine("Enter x:");
// *****************************************************
ldstr "Enter x:" // 把字符装入堆栈
call void [mscorlib]System.Console::WriteLine(string)
// *****************************************************
// s = Console.ReadLine();
// *****************************************************
call string [mscorlib]System.Console::ReadLine()
stloc.3 // 把值存入第3个变量
// *****************************************************
// x = Int32.Parse(s);
// *****************************************************
ldloc.3 // 把第3个变量装入堆栈
// 调用 System.Int32::Parse(string)函数
// 把字符串从堆栈中移除并把解析的结果——int值压入堆栈
call int32 [mscorlib]System.Int32::Parse(string)
stloc.0 // 把值存入第0个变量
// *****************************************************
// 和变量y的一些运算
// *****************************************************
ldstr "Enter y:"
// 装入字符串
call void [mscorlib]System.Console::WriteLine(string)
// 调用
call string [mscorlib]System.Console::ReadLine()
// 调用
stloc.3
//把值存入第3个变量
ldloc.3
//把第3个变量装入堆栈
call int32 [mscorlib]System.Int32::Parse(string)
// 调用
stloc.1
//把值存入第1个变量
// *****************************************************
// z = x + y;
// *****************************************************
ldloc.0 //把第0个变量装入堆栈
ldloc.1 //把第1个变量装入堆栈
// 把这2个值从堆栈中移除,把结果压入堆栈
add
stloc.2 //把值存入第2个变量
// *****************************************************
// Console.Write("x + y = ");
// *****************************************************
ldstr "x + y = " // load string onto stack
call void [mscorlib]System.Console::Write(string)
// *****************************************************
// Console.Write(z);
// *****************************************************
ldloc.2 //把第2个变量装入堆栈
call void [mscorlib]System.Console::Write(int32)
// *****************************************************
// Console.WriteLine("");
// *****************************************************
ldstr "" //装入字符串
call void [mscorlib]System.Console::WriteLine(string)
//相减和相乘运算过程与上面相同
ret
}
休息一下
====================================================================
续上文【翻译】MSIL 教程(一) ,本文继续讲解数组、分支、循环、使用不安全代码和如何调用Win32 API
数组
本程序分配一个int型的数组并给他的元素赋值,然后打印出元素和数组的长度。
命令:
在本例的某些代码片段中,我写了一些注释,以说明堆栈在最后一个变量后的状态。在本例中,我们看到变量由编译器生成,该变量用来调用类的非静态函数。
代码:
.assembly Array1 {}
/*
// This program works as C# code:
int[] x = new int[5];
x[0] = 10;
x[1] = 20;
Console.WriteLine("x[0] = " + x[0].ToString());
Console.WriteLine("x[1] = " + x[1].ToString());
Console.WriteLine("Array length = " + x.Length.ToString());
*/
.method static public void main() il managed
{
.entrypoint
.maxstack 8
.locals init ([0] int32[] x,
[1] int32 tmp) // 由编译器生成
// *****************************************************
// x = new int[5];
// *****************************************************
ldc.i4.5 // 把常量装入堆栈。
// 生成数组,并把他的引用压入堆栈
newarr [mscorlib]System.Int32
// 把数组从堆栈中取出,存入第0个局部变量中
stloc.0
// *****************************************************
// x[0] = 10;
// *****************************************************
ldloc.0 // 把第0个局部变量装入堆栈(数组)
ldc.i4.0 // 把常量0装入堆栈(下标)
ldc.i4.s 10 // 把常量10装入堆栈(值)
stelem.i4 // array[index] = value
// 对数组的其余元素进行同样的操作……
// ***************************************************
// Console.WriteLine("x[0] = " + x[0].ToString());
// ***************************************************
ldstr "x[0] = "
// 堆栈:"x[0] = " (堆栈由局部变量表示)
ldloc.0 // 把第0个变量装入堆栈
ldc.i4.0 // 把第1个变量装入堆栈
// 堆栈: "x[0] = " -> x -> 0
// 把元素的地址装入堆栈
ldelema [mscorlib]System.Int32
// 堆栈: "x[0] = " -> 指向一个Int32的指针
// 10
// 调用实例函数System.Int32::ToString().
call instance string [mscorlib]System.Int32::ToString()
// 堆栈: "x[0] = " -> "10"
// 调用静态函数System.String::Concat(string, string)
call string [mscorlib]System.String::Concat
(string, string)
// 堆栈: "x[0] = 10"
// 调用静态函数 System.Console::WriteLine(string)
call void [mscorlib]System.Console::WriteLine(string)
// 堆栈: 空
//对数组的其余元素进行同样的操作……
// *****************************************************
// Console.WriteLine("Array length = " + x.Length.ToString());
// *****************************************************
ldstr "Array length = "
// 堆栈: "Array length = "
ldloc.0 // 把第0个变量装入堆栈
// 堆栈: "Array length = " -> x
Ldlen // 把数组的长度装入堆栈
// 堆栈: "Array length = " -> 5
conv.i4 // 把栈顶的值转换为Int32,并把他装入堆栈
// 堆栈: "Array length = " -> 5
stloc.1 // 把刚才的值存入第1个局部变量(tmp)
// 堆栈: "Array length = "
ldloca.s tmp //把变量tmp的地址装入堆栈
// 堆栈: "Array length = " -> &tmp
call instance string [mscorlib]System.Int32::ToString()
// 堆栈: "Array length = " -> "5"
call string [mscorlib]System.String::Concat
(string, string)
// 堆栈: "Array length = 5"
call void [mscorlib]System.Console::WriteLine(string)
// 堆栈: 空
ret
}
比较
本程序读取2个数字并打印其最小值。
命令:
本程序的装箱由如下C#程序引起: Console.WriteLine("{0:d}", z);
用这种形式就不会引起装箱: Console.WriteLine(z.ToString());.
代码:
.assembly Compare {}
/*
int x, y, z;
string s;
Console.WriteLine("Enter x:");
s = Console.ReadLine();
x = Int32.Parse(s);
Console.WriteLine("Enter y:");
s = Console.ReadLine();
y = Int32.Parse(s);
if ( x < y )
z = x;
else
z = y;
Console.WriteLine("{0:d}", z);
*/
.method static public void main() il managed
{
.entrypoint
.maxstack 8
.locals init ([0] int32 x,
[1] int32 y,
[2] int32 z,
[3] string s)
// *****************************************************
// Console.WriteLine("Enter x:");
// *****************************************************
ldstr "Enter x:" // 把字符串装入堆栈
call void [mscorlib]System.Console::WriteLine(string)
// *****************************************************
// s = Console.ReadLine();
// *****************************************************
call string [mscorlib]System.Console::ReadLine()
stloc.3 // 保存到第3个变量
// *****************************************************
// x = Int32.Parse(s);
// *****************************************************
ldloc.3 // 把第3个变量装入堆栈
call int32 [mscorlib]System.Int32::Parse(string)
stloc.0 // 保存到第0个变量
// 对y进行相同的操作……
// *****************************************************
// 分支
// if ( x >= y ) goto L_GR;
// *****************************************************
ldloc.0 // 把x装入堆栈(value 1)
ldloc.1 // 把y装入堆栈(value 2)
bge.s L_GR // 跳转到 L_GR 如果value1≥value2
// *****************************************************
// z = x
// *****************************************************
ldloc.0 // 把第0个变量装入堆栈
stloc.2 // 保存到第2个变量
br.s L_CONTINUE // 跳转至 L_CONTINUE
L_GR:
// *****************************************************
// z = y
// *****************************************************
ldloc.1 // 把第1个变量装入堆栈
stloc.2 // 保存到第2个变量
L_CONTINUE:
// *****************************************************
// Console.WriteLine("{0:d}", z);
// 注意:这一行引起装箱操作
// *****************************************************
ldstr "{0:d}" // 把字符串装入堆栈
ldloc.2 // 把第2个变量装入堆栈 (z)
box [mscorlib]System.Int32 // 把Int32变为Object
call void [mscorlib]System.Console::WriteLine(string, object)
ret
}
数组2(循环)
本程序用循环填充一个数组并打印其元素。这一次,我们增加一个静态函数ShowNumber(int), 它在main函数中调用。
命令:
我们可以看到,在本程序中,for 循环在MSIL中用标签来实现。
代码:
.assembly Array2 {}
/*
int[] px = new int[100];
int i;
for ( i = 1; i < 100; i++ )
{
px[i] = i + 1;
}
ShowNumber(px[5]);
ShowNumber(px[10]);
static void ShowNumber(int n)
{
Console.WriteLine(n.ToString());
}
*/
.method static public void main() il managed
{
.entrypoint
.maxstack 8
.locals init ([0] int32[] px,
[1] int32 i)
// *****************************************************
// x = new int[100]
// *****************************************************
ldc.i4.s 100 // 把常量装入堆栈
newarr [mscorlib]System.Int32 // 分配一个Int32型的数组
stloc.0 // 把它存入第0个变量
// *****************************************************
// i = 1
// *****************************************************
ldc.i4.1 //把常量装入堆栈
stloc.1 //把它存入第1个变量
br.s CHECK_COUNTER // 跳转到 CHECK_COUNTER
START_LOOP:
// *****************************************************
// px[i] = i + 1;
// *****************************************************
ldloc.0 // 把第0个变量装入堆栈
// 堆栈: px
ldloc.1 // 把第1个变量装入堆栈
//堆栈; px -> i
ldloc.1 //把第1个变量装入堆栈
//堆栈: px -> i -> i
ldc.i4.1 //把常量装入堆栈
//堆栈: px -> i -> i -> 1.
add // 2个值相加
//堆栈: px -> i -> i+1
// (array,index,value)
stelem.i4 // 把值存入数组元素
//堆栈[index] = value
//堆栈: 空
// *****************************************************
// i = i + 1
// *****************************************************
ldloc.1 //把第1个变量装入堆栈
ldc.i4.1 //把常量装入堆栈
add // 相加
stloc.1 // 把值存入把第1个变量
CHECK_COUNTER:
// *****************************************************
// 如果 i < 100 跳转到循环开始的地方
// *****************************************************
ldloc.1 // 把第1个变量装入堆栈
ldc.i4.s 100 // 把常量装入堆栈
blt.s START_LOOP // 如果value1<value2调转至START_LOOP
// *****************************************************
// ShowNumber(px[5]
// *****************************************************
ldloc.0 // 把第0个变量装入堆栈
// (array)
ldc.i4.5 // 把常量装入堆栈
// (index)
ldelem.i4 // 把数组元素装入堆栈
call void ShowNumber(int32) // 调用 ShowNumber
// *****************************************************
// ShowNumber(px[10]
// *****************************************************
ldloc.0
ldc.i4.s 10
ldelem.i4
call void ShowNumber(int32)
ret
}
.method static public void ShowNumber(int32 n) il managed
{
.maxstack 1
ldarga.s n // 把第n个参数的地址装入堆栈
call instance string [mscorlib]System.Int32::ToString()
call void [mscorlib]System.Console::WriteLine(string)
ret
}
不安全代码
本程序通过unsafe指针填充和打印一个int型数组。
在本程序中,我们将看到新的类型:int32* 和 int32&。使用关键字pinned 可以阻止GC移动由局部指针变量指向的对象。
命令:
Code:
.assembly Unsafe {}
/*
int[] nArray = new int[5];
int i;
int* pCurrent;
fixed ( int* pArray = nArray )
{
pCurrent = pArray;
for ( i = 0; i < 5; i++ )
{
*pCurrent++ = i + 1;
}
}
for ( i = 0; i < 5; i++ )
{
Console.WriteLine(nArray[i].ToString());
}
*/
.method static public void main() il managed
{
.entrypoint
.maxstack 8
.locals ([0] int32[] nArray,
[1] int32 i,
[2] int32* pCurrent,
[3] int32& pinned pArray) // GC不会移动该指针指向的对象
// *****************************************************
// nArray = new int[5];
// *****************************************************
ldc.i4.5 // 把常量5装入堆栈
newarr [mscorlib]System.Int32 // 生成数组 Int32[5]
stloc.0 // 存入第0个变量
// *****************************************************
// pArray = nArray (pArray = &nArray[0])
// *****************************************************
ldloc.0
//把第0个变量装入堆栈(array)
ldc.i4.0
//把常量0装入堆栈(index)
ldelema [mscorlib]System.Int32
// 把array[index]装入堆栈
stloc.3
//存入第3个局部变量
// *****************************************************
// pCurrent = pArray;
// *****************************************************
ldloc.3 //把第3个变量装入堆栈
conv.i // 转变为int
stloc.2 //存入第2个变量
// *****************************************************
// i = 0
// *****************************************************
ldc.i4.0 //把常量0装入堆栈
stloc.1 //存入第1个变量
// *****************************************************
// 跳转到 CHECK_COUNTER
// *****************************************************
br.s CHECK_COUNTER
START_LOOP:
// *****************************************************
// *pCurrent++ = i + 1
// *****************************************************
// 1) 保存pCurrent到堆栈,然后累加pCurrent
ldloc.2
//把第2个变量装入堆栈 [pCurrent]
dup
// 复制栈顶的值
// [pCurrent pCurrent]
ldc.i4.4
// 把常量4装入堆栈 [pCurrent pCurrent 4]
add
// 相加 [pCurrent pCurrent + 4]
stloc.2
// 存入第2个变量 [pCurrent]
// 译注:因为int型指针是4位的,所以加pCurrent+4==*pCurrent++
// 2) 把 (i+1) 保存到pCurrent
ldloc.1
// 把第1个变量装入堆栈 [pCurrent i]
ldc.i4.1
//把常量1装入堆栈 [pCurrent i 1]
add // 相加 [pCurrent i+1]
// 地址 值
stind.i4
// 把i+1的值的地址存入pCurrent [empty]
// *****************************************************
// i = i + 1
// *****************************************************
ldloc.1 // 把第1个变量装入堆栈
ldc.i4.1 // 把常量1装入堆栈
add // 相加
stloc.1 // 存入第1个变量
CHECK_COUNTER:
// *****************************************************
// 如果i < 5 跳转至 START_LOOP;
// *****************************************************
ldloc.1 // 把第1个变量装入堆栈
ldc.i4.5 // 把常量5装入堆栈
blt.s START_LOOP // 如果i<5跳转至START_LOOP
// *****************************************************
// pArray = 0 fixed 块结束
// *****************************************************
ldc.i4.0 // 把常量0装入堆栈
conv.u // 转变为unsigned int,并压入堆栈
stloc.3 // 存入第3个变量
// 打印数组元素……
ret
}
PInvoke
本程序使用Win32 API GetComputerName 和 MessageBox 显示计算机的名字。API的MSIL声明形式如下:
.method public hidebysig static pinvokeimpl("kernel32.dll"
autochar winapi)
int32 GetComputerName(
class [mscorlib]System.Text.StringBuilder
marshal( lptstr) buffer,
int32& size) cil managed preservesig
{
}
.method public hidebysig static pinvokeimpl("User32.dll"
autochar winapi)
int32 MessageBox(native int hWnd,
string marshal( lptstr) lpText,
string marshal( lptstr) lpCaption,
int32 uType) cil managed preservesig
{
}
续上文【翻译】MSIL 教程(二):数组、分支、循环、使用不安全代码和如何调用Win32 API ,本文继续讲解类和异常处理。谨以这三篇译文纪念29年前的今日,那个让母亲今生难以忘记的幸福而又痛苦的日子。
类
在前面的程序中,我们在Main函数中调用类函数,在本程序中,我们将徐希如何定义类。本程序包含2个类: Class1和SampleClass,Class1带有函数Main,在Main中生成SampleClass的一个实例。
指令:
命令:
代码:
.assembly Classes {}
/*
class SampleClass
{
private int m_n;
private string m_s;
public static int nStatic = 10;
public SampleClass(int n, string s)
{
m_n = n;
m_s = s;
}
public int Number
{
get
{
return m_n;
}
}
public string String
{
get
{
return m_s;
}
}
};
class Class1
{
[STAThread]
static voidMain(string[] args)
{
SampleClass o = new SampleClass(1, "Sample");
Console.WriteLine(SampleClass.nStatic.ToString());
Console.WriteLine(o.Number.ToString());
Console.WriteLine(o.String);
}
}
*/
.class private auto ansi beforefieldinit SampleClass
extends [mscorlib]System.Object
{
.field private int32 m_n // private int m_n;
.field private string m_s // private string m_s;
.field public static int32 nStatic // public static int nStatic;
// 该私有静态构造函数由编译器生成
// (用以初始化类的静态成员)
.method private hidebysig specialname rtspecialname static
void .cctor() cil managed
{
.maxstack 8
// *************************************************
// nStatic = 10
// *************************************************
ldc.i4.s 10 // 把常量装入堆栈
// stsfld 命令把静态字段的值替换成堆栈中的值
stsfld int32 SampleClass::nStatic
ret
}
// 构造函数
// public SampleClass(int n, string s)
//
.method public hidebysig specialname rtspecialname
instance void .ctor(int32 n, string s) cil managed
{
.maxstack 8
// *************************************************
// 调用基类的构造函数
// *************************************************
ldarg.0 // 把第0个参数装入堆栈(隐含指针this)
// 调用类Object的构造函数
call instance void [mscorlib]System.Object::.ctor()
// *************************************************
// m_n = n
// *************************************************
ldarg.0 // 把第0个参数装入堆栈(隐含指针this)
ldarg.1 // 把第1个参数装入堆栈(n)
// 把n的值存入this.m_n
stfld int32 SampleClass::m_n
// *************************************************
// m_s = s
// *************************************************
ldarg.0 //把第0个参数装入堆栈(隐含指针this)
ldarg.2 //把第2个参数装入堆栈(s)
// 把s的值存入this.m_s
stfld string SampleClass::m_s
ret
}
// 数字型属性
.property instance int32 Number()
{
// 调用 get_Number
.get instance int32 SampleClass::get_Number()
}
.method public hidebysig specialname instance int32
get_Number() cil managed
{
.maxstack 8
// 由编译器生成的变量
// 译注:实际上,只有Debug版的才有,Release版的就直接返回m_n
.locals ([0] int32 tmp)
// *************************************************
// 返回 m_n;
// *************************************************
ldarg.0
// 装入第0个参数(this)
ldfld int32 SampleClass::m_n
// 装入由堆栈栈顶指针指向的对象的字段
stloc.0
// 存入第0个变量
ldloc.0
// 把第0个变量装入堆栈(函数的返回值)
ret
}
// 字符型属性
.property instance string String()
{
.get instance string SampleClass::get_String()
}
.method public hidebysig specialname instance string
get_String() cil managed
{
.maxstack 8
// 由编译器生成的变量
.locals ([0] string tmp)
ldarg.0
// 装入第0个参数(this)
ldfld string SampleClass::m_s
// 装入由堆栈栈顶指针指向的对象的字段
stloc.0
// 存入第0个变量
ldloc.0
// 把第0个变量装入堆栈(函数的返回值)
ret
}
}
.class private auto ansi beforefieldinit Class1
extends [mscorlib]System.Object
{
// public的缺省构造函数
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
.maxstack 8
// *************************************************
// 调用基类构造函数
// *************************************************
ldarg.0
// 装入thisr
call instance void [mscorlib]System.Object::.ctor()
// 类Objectr的构造函数
ret
}
// Main 函数
.method private hidebysig static void Main(string[] args)
cil managed
{
// 本方法为程序的入口点
.entrypoint
// 自定义属性
.custom instance void [mscorlib]System.
STAThreadAttribute::.ctor() = ( 01 00 00 00 )
.maxstack 8
.locals ([0] class SampleClass o,
[1] int32 tmp) // 由编译器生成
// *************************************************
// o = new SampleClass(1, "Sample");
// *************************************************
ldc.i4.1 // 把常量1装入堆栈
ldstr "Sample" // 把字符常量装入堆栈
// 通过传入堆栈中的2个参数生成一个SampleClass的对象,
// 并把他装入堆栈
newobj instance void SampleClass::.ctor(int32, string)
stloc.0 // 存入第0个变量
// *************************************************
// 访问静态类成员
// Console.WriteLine(SampleClass.nStatic.ToString());
// *************************************************
//把静态字段的地址装入堆栈
ldsflda int32 SampleClass::nStatic
// 为堆栈中的对象调用Int32::ToString
call instance string [mscorlib]System.Int32
::ToString()
// 调用静态的WriteLine,其传入参数是堆栈中的字符串
call void [mscorlib]System.Console
::WriteLine(string)
// *************************************************
// 调用实例函数
// Console.WriteLine(o.Number.ToString());
// *************************************************
ldloc.0 // 装入第0个变量
// 调用堆栈中对象的函数
call instance int32 SampleClass::get_Number()
stloc.1 // 存入第1个变量
ldloca.s tmp // 把地址装入堆栈
call instance string [mscorlib]System.Int32
::ToString()
call void [mscorlib]System.Console
::WriteLine(string)
// *************************************************
// 调用实例函数
// Console.WriteLine(o.String);
// *************************************************
ldloc.0
callvirt instance string SampleClass::get_String()
call void [mscorlib]System.Console
::WriteLine(string)
// *************************************************
ldstr "Press Enter to continue"
call void [mscorlib]System.Console
::WriteLine(class System.String)
call int32 [mscorlib]System.Console::Read()
pop
// *************************************************
ret
}
}
异常处理
本程序使2个数相除,捕捉其除0异常。try/catch 块在MSIL中看起来像C#中的一样。
命令:
代码:
.assembly Exception {}
/*
int x, y, z;
string s;
Console.WriteLine("Enter x:");
s = Console.ReadLine();
x = Int32.Parse(s);
Console.WriteLine("Enter y:");
s = Console.ReadLine();
y = Int32.Parse(s);
try
{
z = x / y;
Console.WriteLine(z.ToString());
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
*/
.method static public void main() il managed
{
.entrypoint
.maxstack 8
.locals ([0] int32 x,
[1] int32 y,
[2] int32 z,
[3] string s,
[4] class [mscorlib]System.Exception e)
//输入 x, y ...
.try
{
// *************************************************
// z = x / y;
// *************************************************
ldloc.0 // 装入第0个变量
ldloc.1 // 装入第1个变量
div // 相除
stloc.2 // 把结果存入第2个变量
// *************************************************
// Console.WriteLine(z.ToString());
// *************************************************
ldloca.s z // 装入z的地址
call instance string [mscorlib]System.Int32
::ToString()
call void [mscorlib]System.Console
::WriteLine(string)
leave.s END_TRY_CATCH // 退出try
}
catch [mscorlib]System.Exception
{
stloc.s e // 存入由堆栈抛出的异常
// *************************************************
// Console.WriteLine(e.Message);
// *************************************************
ldloc.s e // load e
callvirt instance string [mscorlib]System.Exception
::get_Message()
call void [mscorlib]System.Console
::WriteLine(string)
leave.s END_TRY_CATCH // 退出catch块
}
END_TRY_CATCH:
ret
}
完
转自:http://blog.csdn.net/mallva/article/details/2215976
转自:http://www.cnblogs.com/Yahong111/archive/2007/08/16/857771.html