看下面这个例子:
using System;
using System.Collections.Generic;
namespace ConsoleApplication3
{
class Program
{
delegate void Printer();
//代理相当于一个类型
static void Print1()
{
Console.WriteLine("print1");
}
static void Print2()
{
Console.WriteLine("print2");
}
static void Print3()
{
Console.WriteLine("print3");
}
static void Main(string[] args)
{
Printer p = Print1;
//方法到一个兼容委托类型的隐式转换
//相当于复制构造函数,然而c#是没有什么隐式类型转换这种说法的
p += Print2;//不管你加不加这后面的两个方法,Printer依然生成继承 MulticastDelegate 的类
p += Print3;
p.Invoke();
}
}
}
首先说一个概念,托管代码:
托管代码就是Visual Basic .NET和C#编译器编译出来的代码。编译器把代码编译成中间语言(IL),而不是能直接在你的电脑上运行的机器码。IL是独立于CPU且面向对象的指令集。
中间语言IL被封装在一个叫程序集 (assembly)的文件中,程序集中包含了描述你所创建的类,方法和属性(例如安全需求)的所有元数据。
用IL DASM 反汇编这个程序:
工具界面上面的一些标识的含义:
所以结合IL我们可以看出来:
这里表示的就是Printer类的详细信息
逐步分析:
.class auto ansi sealed nested private Printer
extends [mscorlib]System.MulticastDelegate
{
} // end of class Printer
.class 表示Program是一个类。并且它继承自程序集—mscorlib的System. MulticastDelegate类
Auto 程序的加载是由CLR来管理内存的
CLR的核心功能:内存管理,程序集加载,安全性,异常处理,线程同步等等。
CLR是公共语言运行库(Common Language Runtime)和Java虚拟机一样也是一个运行时环境
ansi,是为了在没有托管代码与托管代码之间实现无缝转换。这里主要指C、C++代码等
sealed 不可被继承 nested 嵌套类
再来看看更上层的Program类的详细信息:
.class private auto ansi beforefieldinit ConsoleApplication3.Program
extends [mscorlib]System.Object
{
} // end of class ConsoleApplication3.Program
Beforefieldinit是用来标记运行库(CLR)可以在静态字段方法生成后的任意时刻,来加载构造函数,否则CLR就需要在一个精准的时间加载构造函数
接下来看一下复制构造函数
.method public hidebysig specialname rtspecialname
instance void .ctor(object 'object',
native int 'method') runtime managed
{
} // end of method Printer::.ctor
Hidebysig表示当把此类作为基类,存在派生类时,此方法不被继承,同上构造函数
cil managed:表示其中为IL代码,指示编译器编译为托管代码(上面写过的概念)
再来看普通构造函数:
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// 代码大小 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method Program::.ctor
.maxstack:表示调用构造函数.otor期间的评估堆栈(Evaluation Stack)
IL_0000:标记代码行开头
ldarg.0:表示转载第一个成员参数,在这里其实是这个对象的this指针的引用
call:call一般用于调用静态方法,因为静态方法是在编译期就确定的。而这里的构造函数.ctor()也是在编译期就制定的。
而另一指令callvirt则表示调用实例方法,它是在运行时确定的,因为如前述,当调用方法的继承关系时,就要比较基类与派生类的同名函数的实现方法(virtual和new),以确定调用的函数所属的Method Table
call与callvirt: call主要用来调用静态方法,callvirt则用来调用普通方法和需要运行时绑定的方法(也就是用instance标记的实例方法)。不过也存在特殊情况,那就是call去调用虚方法,比如在密封类中的虚方法因为一定不可能会被重写因此使用call可提高性能。为什么会提高性能呢?不知道你是否还记得创建一个对象去调用这个对象的方法时,我们经常会判断这个对象是否为null,如果这个对象为null时去调用方法则会报错。之所以出现这种情况是因为callvirt在调用方法时会进行类型检测,此外判断是否有子类方法覆盖的情况从而动态绑定方法,而采用call则直接去调用了。另外当调用基类的虚方法时,比如调用object.ToString方法就是采用call方法,如果采用callvirt的话因为有可能要查看子类(一直查看到最后一个继承父类的子类)是否有重写方法,从而降低了性能。不过说到底call用来调用静态方法,而callvirt调用与对象关联的动态方法的核心思想是可以肯定的,那些采用call的特殊情况都是因为在这种情况下根本不需要动态绑定方法而是可以直接使用的
ret:表示执行完毕,返回
print1():最简单的一个函数
static void Print1(){Console.WriteLine("print1"); }
.method private hidebysig static void Print1() cil managed
{
// 代码大小 13 (0xd)
.maxstack 8
IL_0000: nop
IL_0001: ldstr "print1"
IL_0006: call void [mscorlib]System.Console::WriteLine(string)
IL_000b: nop
IL_000c: ret
} // end of method Program::Print1
ldstr:表示将字符串压栈,是用来把一个字符串加载到内存或评估堆栈中。在我们使用这些变量之前,是需要把这些变量加载到评估堆栈(evaluation stack )中去的,实际上是将字符串引用加载到栈中而不是用newobj
.NET运行时任何有意义的操作都是在堆栈上完成的,而不是直接操作寄存器。这就为.NET跨平台打下了基础
在IL中压栈通常以ld开头,出栈则以st开头
最后是main函数:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint //程序入口
// 代码大小 70 (0x46)
.maxstack 3 //计算堆栈大小
.locals init ([0] class ConsoleApplication3.Program/Printer p)
IL_0000: nop
IL_0001: ldnull //将空引用(O 类型)推送到计算堆栈上
IL_0002: ldftn void ConsoleApplication3.Program::Print1()
//将指向实现特定方法的本机代码的非托管指针(native int 类型)推送到计算堆栈上。
IL_0008: newobj instance void ConsoleApplication3.Program/Printer::.ctor(object, native int)
//构造Printer
// C#中使用new创建一个对象时则在IL中对应的是newobj,另外还有值类型也是可以通过new来创建的,不过在IL中它对应的则是initobj
// newobj用来创建一个对象,首先会分配这个对象所需的内存,接着初始化对象附加成员同步索引块和类型对象指针然后再执行构造函数进行初始化并返回对象引用。initobj则是完成栈上已经分配好的内存的初始化工作,将值类型置0引用类型置null即可。
IL_000d: stloc.0 //把计算堆栈顶部的值放到调用堆栈索引0处
IL_000e: ldloc.0 //把调用堆栈索引为0处的值复制到计算堆栈
IL_000f: ldnull
IL_0010: ldftn void ConsoleApplication3.Program::Print2()
IL_0016: newobj instance void ConsoleApplication3.Program/Printer::.ctor(object, native int)
//又构造了一个Printer
IL_001b: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate)
//调用System.Delegate::Combine,把两个System.Delegate合并
//源码里面的 “+=”就是在这里实现的
IL_0020: castclass ConsoleApplication3.Program/Printer
//尝试将引用传递的对象转换为指定的类
IL_0025: stloc.0
IL_0026: ldloc.0
IL_0027: ldnull
IL_0028: ldftn void ConsoleApplication3.Program::Print3()
IL_002e: newobj instance void ConsoleApplication3.Program/Printer::.ctor(object,
native int)
IL_0033: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,
class [mscorlib]System.Delegate)
IL_0038: castclass ConsoleApplication3.Program/Printer
IL_003d: stloc.0
IL_003e: ldloc.0
IL_003f: callvirt instance void ConsoleApplication3.Program/Printer::Invoke()
// 对对象调用后期绑定方法,并且将返回值推送到计算堆栈上
IL_0044: nop
IL_0045: ret
} // end of method Program::Main
总结一下常用的IL指令:
.entrypoint:指令表示CLR加载程序时,是首先从.entrypoint开始的,即从Main方法作为程序的入口函数
stloc.X:把计算堆栈顶部的值放到调用堆栈索引为X处
ldloc.X:把调用堆栈X处的值复制到计算堆栈
.newobj: 用于创建引用类型的对象;
.ldstr:用于创建String对象变量;
.newarr:用于创建数组型对象;
.box:在值类型转换为引用类型的对象时,将值类型拷贝至托管堆上分配内存。
.assembly:指令告诉编译器,我们准备去用一个外部的类库(不是我们自己写的,而是提前编译好的
对于流程控制,主要是br、brture和brfalse这3条指令,其中br是直接进行跳转,brture和brture则是进行判断再进行跳转。
具体内容参考:https://www.cnblogs.com/fangyz/p/5547433.html
一个像exe这样的程序集,结构如下图:
一个程序集是有多个托管模块组成的,一个模块可以理解为一个类或者多个类一起编译后生成的程序集
程序集清单指的是描述程序集的相关信息,PE文件头描述PE文件的文件类型、创建时间等。CLR头描述CLR版本、CPU信息等,它告诉系统这是一个.NET程序集
元数据用来描述类、方法、参数、属性等数据,.NET中每个模块包含44个元数据表,主要包括定义表、引用表、指针表和堆。定义表包括类定义表、方法表等,引用表描述引用到类型或方法之间的映射记录,指针表里存放着方法指针、参数指针等。元数据表就相当于一个数据库,多张表之间有类似于主外键之间的关系
我们调用一个方法表中的方法,这个方法会指向一个触发JIT编译器地址和方法对应的IL地址,于是JIT编译器便将这个方法指向的IL编译成本地代码。生成本地代码后这个方法将会有一条引用指向本地代码首地址,这样下次调用这个方法的时候将直接执行指向的本地代码
名称 |
说明 |
Add |
将两个值相加并将结果推送到计算堆栈上。 |
Add.Ovf |
将两个整数相加,执行溢出检查,并且将结果推送到计算堆栈上。 |
Add.Ovf.Un |
将两个无符号整数值相加,执行溢出检查,并且将结果推送到计算堆栈上。 |
And |
计算两个值的按位“与”并将结果推送到计算堆栈上。 |
Arglist |
返回指向当前方法的参数列表的非托管指针。 |
Beq |
如果两个值相等,则将控制转移到目标指令。 |
Beq.S |
如果两个值相等,则将控制转移到目标指令(短格式)。 |
Bge |
如果第一个值大于或等于第二个值,则将控制转移到目标指令。 |
Bge.S |
如果第一个值大于或等于第二个值,则将控制转移到目标指令(短格式)。 |
Bge.Un |
当比较无符号整数值或不可排序的浮点型值时,如果第一个值大于第二个值,则将控制转移到目标指令。 |
Bge.Un.S |
当比较无符号整数值或不可排序的浮点型值时,如果第一个值大于第二个值,则将控制转移到目标指令(短格式)。 |
Bgt |
如果第一个值大于第二个值,则将控制转移到目标指令。 |
Bgt.S |
如果第一个值大于第二个值,则将控制转移到目标指令(短格式)。 |
Bgt.Un |
当比较无符号整数值或不可排序的浮点型值时,如果第一个值大于第二个值,则将控制转移到目标指令。 |
Bgt.Un.S |
当比较无符号整数值或不可排序的浮点型值时,如果第一个值大于第二个值,则将控制转移到目标指令(短格式)。 |
Ble |
如果第一个值小于或等于第二个值,则将控制转移到目标指令。 |
Ble.S |
如果第一个值小于或等于第二个值,则将控制转移到目标指令(短格式)。 |
Ble.Un |
当比较无符号整数值或不可排序的浮点型值时,如果第一个值小于或等于第二个值,则将控制转移到目标指令。 |
Ble.Un.S |
当比较无符号整数值或不可排序的浮点值时,如果第一个值小于或等于第二个值,则将控制权转移到目标指令(短格式)。 |
Blt |
如果第一个值小于第二个值,则将控制转移到目标指令。 |
Blt.S |
如果第一个值小于第二个值,则将控制转移到目标指令(短格式)。 |
Blt.Un |
当比较无符号整数值或不可排序的浮点型值时,如果第一个值小于第二个值,则将控制转移到目标指令。 |
Blt.Un.S |
当比较无符号整数值或不可排序的浮点型值时,如果第一个值小于第二个值,则将控制转移到目标指令(短格式)。 |
Bne.Un |
当两个无符号整数值或不可排序的浮点型值不相等时,将控制转移到目标指令。 |
Bne.Un.S |
当两个无符号整数值或不可排序的浮点型值不相等时,将控制转移到目标指令(短格式)。 |
Box |
将值类转换为对象引用(O 类型)。 |
Br |
无条件地将控制转移到目标指令。 |
Br.S |
无条件地将控制转移到目标指令(短格式)。 |
Break |
向公共语言结构 (CLI) 发出信号以通知调试器已撞上了一个断点。 |
Brfalse |
如果 value 为 false、空引用(Visual Basic 中的 Nothing)或零,则将控制转移到目标指令。 |
Brfalse.S |
如果 value 为 false、空引用或零,则将控制转移到目标指令。 |
Brtrue |
如果 value 为 true、非空或非零,则将控制转移到目标指令。 |
Brtrue.S |
如果 value 为 true、非空或非零,则将控制转移到目标指令(短格式)。 |
Call |
调用由传递的方法说明符指示的方法。 |
Calli |
通过调用约定描述的参数调用在计算堆栈上指示的方法(作为指向入口点的指针)。 |
Callvirt |
对对象调用后期绑定方法,并且将返回值推送到计算堆栈上。 |
Castclass |
尝试将引用传递的对象转换为指定的类。 |
Ceq |
比较两个值。如果这两个值相等,则将整数值 1 (int32) 推送到计算堆栈上;否则,将 0 (int32) 推送到计算堆栈上。 |
Cgt |
比较两个值。如果第一个值大于第二个值,则将整数值 1 (int32) 推送到计算堆栈上;反之,将 0 (int32) 推送到计算堆栈上。 |
Cgt.Un |
比较两个无符号的或不可排序的值。如果第一个值大于第二个值,则将整数值 1 (int32) 推送到计算堆栈上;反之,将 0 (int32) 推送到计算堆栈上。 |
Ckfinite |
如果值不是有限数,则引发 ArithmeticException。 |
Clt |
比较两个值。如果第一个值小于第二个值,则将整数值 1 (int32) 推送到计算堆栈上;反之,将 0 (int32) 推送到计算堆栈上。 |
Clt.Un |
比较无符号的或不可排序的值 value1 和 value2。如果 value1 小于 value2,则将整数值 1 (int32 ) 推送到计算堆栈上;反之,将 0 ( int32 ) 推送到计算堆栈上。 |
Constrained |
约束要对其进行虚方法调用的类型。 |
Conv.I |
将位于计算堆栈顶部的值转换为 native int。 |
Conv.I1 |
将位于计算堆栈顶部的值转换为 int8,然后将其扩展(填充)为 int32。 |
Conv.I2 |
将位于计算堆栈顶部的值转换为 int16,然后将其扩展(填充)为 int32。 |
Conv.I4 |
将位于计算堆栈顶部的值转换为 int32。 |
Conv.I8 |
将位于计算堆栈顶部的值转换为 int64。 |
Conv.Ovf.I |
将位于计算堆栈顶部的有符号值转换为有符号 native int,并在溢出时引发 OverflowException。 |
Conv.Ovf.I.Un |
将位于计算堆栈顶部的无符号值转换为有符号 native int,并在溢出时引发 OverflowException。 |
Conv.Ovf.I1 |
将位于计算堆栈顶部的有符号值转换为有符号 int8 并将其扩展为 int32,并在溢出时引发 OverflowException。 |
Conv.Ovf.I1.Un |
将位于计算堆栈顶部的无符号值转换为有符号 int8 并将其扩展为 int32,并在溢出时引发 OverflowException。 |
Conv.Ovf.I2 |
将位于计算堆栈顶部的有符号值转换为有符号 int16 并将其扩展为 int32,并在溢出时引发 OverflowException。 |
Conv.Ovf.I2.Un |
将位于计算堆栈顶部的无符号值转换为有符号 int16 并将其扩展为 int32,并在溢出时引发 OverflowException。 |
Conv.Ovf.I4 |
将位于计算堆栈顶部的有符号值转换为有符号 int32,并在溢出时引发 OverflowException。 |
Conv.Ovf.I4.Un |
将位于计算堆栈顶部的无符号值转换为有符号 int32,并在溢出时引发 OverflowException。 |
Conv.Ovf.I8 |
将位于计算堆栈顶部的有符号值转换为有符号 int64,并在溢出时引发 OverflowException。 |
Conv.Ovf.I8.Un |
将位于计算堆栈顶部的无符号值转换为有符号 int64,并在溢出时引发 OverflowException。 |
Conv.Ovf.U |
将位于计算堆栈顶部的有符号值转换为 unsigned native int,并在溢出时引发 OverflowException。 |
Conv.Ovf.U.Un |
将位于计算堆栈顶部的无符号值转换为 unsigned native int,并在溢出时引发 OverflowException。 |
Conv.Ovf.U1 |
将位于计算堆栈顶部的有符号值转换为 unsigned int8 并将其扩展为 int32,并在溢出时引发 OverflowException。 |
Conv.Ovf.U1.Un |
将位于计算堆栈顶部的无符号值转换为 unsigned int8 并将其扩展为 int32,并在溢出时引发 OverflowException。 |
Conv.Ovf.U2 |
将位于计算堆栈顶部的有符号值转换为 unsigned int16 并将其扩展为 int32,并在溢出时引发 OverflowException。 |
Conv.Ovf.U2.Un |
将位于计算堆栈顶部的无符号值转换为 unsigned int16 并将其扩展为 int32,并在溢出时引发 OverflowException。 |
Conv.Ovf.U4 |
将位于计算堆栈顶部的有符号值转换为 unsigned int32,并在溢出时引发 OverflowException。 |
Conv.Ovf.U4.Un |
将位于计算堆栈顶部的无符号值转换为 unsigned int32,并在溢出时引发 OverflowException。 |
Conv.Ovf.U8 |
将位于计算堆栈顶部的有符号值转换为 unsigned int64,并在溢出时引发 OverflowException。 |
Conv.Ovf.U8.Un |
将位于计算堆栈顶部的无符号值转换为 unsigned int64,并在溢出时引发 OverflowException。 |
Conv.R.Un |
将位于计算堆栈顶部的无符号整数值转换为 float32。 |
Conv.R4 |
将位于计算堆栈顶部的值转换为 float32。 |
Conv.R8 |
将位于计算堆栈顶部的值转换为 float64。 |
Conv.U |
将位于计算堆栈顶部的值转换为 unsigned native int,然后将其扩展为 native int。 |
Conv.U1 |
将位于计算堆栈顶部的值转换为 unsigned int8,然后将其扩展为 int32。 |
Conv.U2 |
将位于计算堆栈顶部的值转换为 unsigned int16,然后将其扩展为 int32。 |
Conv.U4 |
将位于计算堆栈顶部的值转换为 unsigned int32,然后将其扩展为 int32。 |
Conv.U8 |
将位于计算堆栈顶部的值转换为 unsigned int64,然后将其扩展为 int64。 |
Cpblk |
将指定数目的字节从源地址复制到目标地址。 |
Cpobj |
将位于对象(&、* 或 native int 类型)地址的值类型复制到目标对象(&、* 或 native int 类型)的地址。 |
Div |
将两个值相除并将结果作为浮点(F 类型)或商(int32 类型)推送到计算堆栈上。 |
Div.Un |
两个无符号整数值相除并将结果 ( int32 ) 推送到计算堆栈上。 |
Dup |
复制计算堆栈上当前最顶端的值,然后将副本推送到计算堆栈上。 |
Endfilter |
将控制从异常的 filter 子句转移回公共语言结构 (CLI) 异常处理程序。 |
Endfinally |
将控制从异常块的 fault 或 finally 子句转移回公共语言结构 (CLI) 异常处理程序。 |
Initblk |
将位于特定地址的内存的指定块初始化为给定大小和初始值。 |
Initobj |
将位于指定地址的值类型的每个字段初始化为空引用或适当的基元类型的 0。 |
Isinst |
测试对象引用(O 类型)是否为特定类的实例。 |
Jmp |
退出当前方法并跳至指定方法。 |
Ldarg |
将参数(由指定索引值引用)加载到堆栈上。 |
Ldarg.0 |
将索引为 0 的参数加载到计算堆栈上。 |
Ldarg.1 |
将索引为 1 的参数加载到计算堆栈上。 |
Ldarg.2 |
将索引为 2 的参数加载到计算堆栈上。 |
Ldarg.3 |
将索引为 3 的参数加载到计算堆栈上。 |
Ldarg.S |
将参数(由指定的短格式索引引用)加载到计算堆栈上。 |
Ldarga |
将参数地址加载到计算堆栈上。 |
Ldarga.S |
以短格式将参数地址加载到计算堆栈上。 |
Ldc.I4 |
将所提供的 int32 类型的值作为 int32 推送到计算堆栈上。 |
Ldc.I4.0 |
将整数值 0 作为 int32 推送到计算堆栈上。 |
Ldc.I4.1 |
将整数值 1 作为 int32 推送到计算堆栈上。 |
Ldc.I4.2 |
将整数值 2 作为 int32 推送到计算堆栈上。 |
Ldc.I4.3 |
将整数值 3 作为 int32 推送到计算堆栈上。 |
Ldc.I4.4 |
将整数值 4 作为 int32 推送到计算堆栈上。 |
Ldc.I4.5 |
将整数值 5 作为 int32 推送到计算堆栈上。 |
Ldc.I4.6 |
将整数值 6 作为 int32 推送到计算堆栈上。 |
Ldc.I4.7 |
将整数值 7 作为 int32 推送到计算堆栈上。 |
Ldc.I4.8 |
将整数值 8 作为 int32 推送到计算堆栈上。 |
Ldc.I4.M1 |
将整数值 -1 作为 int32 推送到计算堆栈上。 |
Ldc.I4.S |
将提供的 int8 值作为 int32 推送到计算堆栈上(短格式)。 |
Ldc.I8 |
将所提供的 int64 类型的值作为 int64 推送到计算堆栈上。 |
Ldc.R4 |
将所提供的 float32 类型的值作为 F (float) 类型推送到计算堆栈上。 |
Ldc.R8 |
将所提供的 float64 类型的值作为 F (float) 类型推送到计算堆栈上。 |
Ldelem |
按照指令中指定的类型,将指定数组索引中的元素加载到计算堆栈的顶部。 |
Ldelem.I |
将位于指定数组索引处的 native int 类型的元素作为 native int 加载到计算堆栈的顶部。 |
Ldelem.I1 |
将位于指定数组索引处的 int8 类型的元素作为 int32 加载到计算堆栈的顶部。 |
Ldelem.I2 |
将位于指定数组索引处的 int16 类型的元素作为 int32 加载到计算堆栈的顶部。 |
Ldelem.I4 |
将位于指定数组索引处的 int32 类型的元素作为 int32 加载到计算堆栈的顶部。 |
Ldelem.I8 |
将位于指定数组索引处的 int64 类型的元素作为 int64 加载到计算堆栈的顶部。 |
Ldelem.R4 |
将位于指定数组索引处的 float32 类型的元素作为 F 类型(浮点型)加载到计算堆栈的顶部。 |
Ldelem.R8 |
将位于指定数组索引处的 float64 类型的元素作为 F 类型(浮点型)加载到计算堆栈的顶部。 |
Ldelem.Ref |
将位于指定数组索引处的包含对象引用的元素作为 O 类型(对象引用)加载到计算堆栈的顶部。 |
Ldelem.U1 |
将位于指定数组索引处的 unsigned int8 类型的元素作为 int32 加载到计算堆栈的顶部。 |
Ldelem.U2 |
将位于指定数组索引处的 unsigned int16 类型的元素作为 int32 加载到计算堆栈的顶部。 |
Ldelem.U4 |
将位于指定数组索引处的 unsigned int32 类型的元素作为 int32 加载到计算堆栈的顶部。 |
Ldelema |
将位于指定数组索引的数组元素的地址作为 & 类型(托管指针)加载到计算堆栈的顶部。 |
Ldfld |
查找对象中其引用当前位于计算堆栈的字段的值。 |
Ldflda |
查找对象中其引用当前位于计算堆栈的字段的地址。 |
Ldftn |
将指向实现特定方法的本机代码的非托管指针(native int 类型)推送到计算堆栈上。 |
Ldind.I |
将 native int 类型的值作为 native int 间接加载到计算堆栈上。 |
Ldind.I1 |
将 int8 类型的值作为 int32 间接加载到计算堆栈上。 |
Ldind.I2 |
将 int16 类型的值作为 int32 间接加载到计算堆栈上。 |
Ldind.I4 |
将 int32 类型的值作为 int32 间接加载到计算堆栈上。 |
Ldind.I8 |
将 int64 类型的值作为 int64 间接加载到计算堆栈上。 |
Ldind.R4 |
将 float32 类型的值作为 F (float) 类型间接加载到计算堆栈上。 |
Ldind.R8 |
将 float64 类型的值作为 F (float) 类型间接加载到计算堆栈上。 |
Ldind.Ref |
将对象引用作为 O(对象引用)类型间接加载到计算堆栈上。 |
Ldind.U1 |
将 unsigned int8 类型的值作为 int32 间接加载到计算堆栈上。 |
Ldind.U2 |
将 unsigned int16 类型的值作为 int32 间接加载到计算堆栈上。 |
Ldind.U4 |
将 unsigned int32 类型的值作为 int32 间接加载到计算堆栈上。 |
Ldlen |
将从零开始的、一维数组的元素的数目推送到计算堆栈上。 |
Ldloc |
将指定索引处的局部变量加载到计算堆栈上。 |
Ldloc.0 |
将索引 0 处的局部变量加载到计算堆栈上。 |
Ldloc.1 |
将索引 1 处的局部变量加载到计算堆栈上。 |
Ldloc.2 |
将索引 2 处的局部变量加载到计算堆栈上。 |
Ldloc.3 |
将索引 3 处的局部变量加载到计算堆栈上。 |
Ldloc.S |
将特定索引处的局部变量加载到计算堆栈上(短格式)。 |
Ldloca |
将位于特定索引处的局部变量的地址加载到计算堆栈上。 |
Ldloca.S |
将位于特定索引处的局部变量的地址加载到计算堆栈上(短格式)。 |
Ldnull |
将空引用(O 类型)推送到计算堆栈上。 |
Ldobj |
将地址指向的值类型对象复制到计算堆栈的顶部。 |
Ldsfld |
将静态字段的值推送到计算堆栈上。 |
Ldsflda |
将静态字段的地址推送到计算堆栈上。 |
Ldstr |
推送对元数据中存储的字符串的新对象引用。 |
Ldtoken |
将元数据标记转换为其运行时表示形式,并将其推送到计算堆栈上。 |
Ldvirtftn |
将指向实现与指定对象关联的特定虚方法的本机代码的非托管指针(native int 类型)推送到计算堆栈上。 |
Leave |
退出受保护的代码区域,无条件将控制转移到特定目标指令。 |
Leave.S |
退出受保护的代码区域,无条件将控制转移到目标指令(缩写形式)。 |
Localloc |
从本地动态内存池分配特定数目的字节并将第一个分配的字节的地址(瞬态指针,* 类型)推送到计算堆栈上。 |
Mkrefany |
将对特定类型实例的类型化引用推送到计算堆栈上。 |
Mul |
将两个值相乘并将结果推送到计算堆栈上。 |
Mul.Ovf |
将两个整数值相乘,执行溢出检查,并将结果推送到计算堆栈上。 |
Mul.Ovf.Un |
将两个无符号整数值相乘,执行溢出检查,并将结果推送到计算堆栈上。 |
Neg |
对一个值执行求反并将结果推送到计算堆栈上。 |
Newarr |
将对新的从零开始的一维数组(其元素属于特定类型)的对象引用推送到计算堆栈上。 |
Newobj |
创建一个值类型的新对象或新实例,并将对象引用(O 类型)推送到计算堆栈上。 |
Nop |
如果修补操作码,则填充空间。尽管可能消耗处理周期,但未执行任何有意义的操作。 |
Not |
计算堆栈顶部整数值的按位求补并将结果作为相同的类型推送到计算堆栈上。 |
Or |
计算位于堆栈顶部的两个整数值的按位求补并将结果推送到计算堆栈上。 |
Pop |
移除当前位于计算堆栈顶部的值。 |
Prefix1 |
基础结构。此指令为保留指令。 |
Prefix2 |
基础结构。此指令为保留指令。 |
Prefix3 |
基础结构。此指令为保留指令。 |
Prefix4 |
基础结构。此指令为保留指令。 |
Prefix5 |
基础结构。此指令为保留指令。 |
Prefix6 |
基础结构。此指令为保留指令。 |
Prefix7 |
基础结构。此指令为保留指令。 |
Prefixref |
基础结构。此指令为保留指令。 |
Readonly |
指定后面的数组地址操作在运行时不执行类型检查,并且返回可变性受限的托管指针。 |
Refanytype |
检索嵌入在类型化引用内的类型标记。 |
Refanyval |
检索嵌入在类型化引用内的地址(& 类型)。 |
Rem |
将两个值相除并将余数推送到计算堆栈上。 |
Rem.Un |
将两个无符号值相除并将余数推送到计算堆栈上。 |
Ret |
从当前方法返回,并将返回值(如果存在)从调用方的计算堆栈推送到被调用方的计算堆栈上。 |
Rethrow |
再次引发当前异常。 |
Shl |
将整数值左移(用零填充)指定的位数,并将结果推送到计算堆栈上。 |
Shr |
将整数值右移(保留符号)指定的位数,并将结果推送到计算堆栈上。 |
Shr.Un |
将无符号整数值右移(用零填充)指定的位数,并将结果推送到计算堆栈上。 |
Sizeof |
将提供的值类型的大小(以字节为单位)推送到计算堆栈上。 |
Starg |
将位于计算堆栈顶部的值存储到位于指定索引的参数槽中。 |
Starg.S |
将位于计算堆栈顶部的值存储在参数槽中的指定索引处(短格式)。 |
Stelem |
用计算堆栈中的值替换给定索引处的数组元素,其类型在指令中指定。 |
Stelem.I |
用计算堆栈上的 native int 值替换给定索引处的数组元素。 |
Stelem.I1 |
用计算堆栈上的 int8 值替换给定索引处的数组元素。 |
Stelem.I2 |
用计算堆栈上的 int16 值替换给定索引处的数组元素。 |
Stelem.I4 |
用计算堆栈上的 int32 值替换给定索引处的数组元素。 |
Stelem.I8 |
用计算堆栈上的 int64 值替换给定索引处的数组元素。 |
Stelem.R4 |
用计算堆栈上的 float32 值替换给定索引处的数组元素。 |
Stelem.R8 |
用计算堆栈上的 float64 值替换给定索引处的数组元素。 |
Stelem.Ref |
用计算堆栈上的对象 ref 值(O 类型)替换给定索引处的数组元素。 |
Stfld |
用新值替换在对象引用或指针的字段中存储的值。 |
Stind.I |
在所提供的地址存储 native int 类型的值。 |
Stind.I1 |
在所提供的地址存储 int8 类型的值。 |
Stind.I2 |
在所提供的地址存储 int16 类型的值。 |
Stind.I4 |
在所提供的地址存储 int32 类型的值。 |
Stind.I8 |
在所提供的地址存储 int64 类型的值。 |
Stind.R4 |
在所提供的地址存储 float32 类型的值。 |
Stind.R8 |
在所提供的地址存储 float64 类型的值。 |
Stind.Ref |
存储所提供地址处的对象引用值。 |
Stloc |
从计算堆栈的顶部弹出当前值并将其存储到指定索引处的局部变量列表中。 |
Stloc.0 |
从计算堆栈的顶部弹出当前值并将其存储到索引 0 处的局部变量列表中。 |
Stloc.1 |
从计算堆栈的顶部弹出当前值并将其存储到索引 1 处的局部变量列表中。 |
Stloc.2 |
从计算堆栈的顶部弹出当前值并将其存储到索引 2 处的局部变量列表中。 |
Stloc.3 |
从计算堆栈的顶部弹出当前值并将其存储到索引 3 处的局部变量列表中。 |
Stloc.S |
从计算堆栈的顶部弹出当前值并将其存储在局部变量列表中的 index 处(短格式)。 |
Stobj |
将指定类型的值从计算堆栈复制到所提供的内存地址中。 |
Stsfld |
用来自计算堆栈的值替换静态字段的值。 |
Sub |
从其他值中减去一个值并将结果推送到计算堆栈上。 |
Sub.Ovf |
从另一值中减去一个整数值,执行溢出检查,并且将结果推送到计算堆栈上。 |
Sub.Ovf.Un |
从另一值中减去一个无符号整数值,执行溢出检查,并且将结果推送到计算堆栈上。 |
Switch |
实现跳转表。 |
Tailcall |
执行后缀的方法调用指令,以便在执行实际调用指令前移除当前方法的堆栈帧。 |
Throw |
引发当前位于计算堆栈上的异常对象。 |
Unaligned |
指示当前位于计算堆栈上的地址可能没有与紧接的 ldind、stind、ldfld、stfld、ldobj、stobj、initblk 或 cpblk 指令的自然大小对齐。 |
Unbox |
将值类型的已装箱的表示形式转换为其未装箱的形式。 |
Unbox.Any |
将指令中指定类型的已装箱的表示形式转换成未装箱形式。 |
Volatile |
指定当前位于计算堆栈顶部的地址可以是易失的,并且读取该位置的结果不能被缓存,或者对该地址的多个存储区不能被取消。 |
Xor |
计算位于计算堆栈顶部的两个值的按位异或,并且将结果推送到计算堆栈上。 |