所谓动态方法,是指在程序运行阶段生成的并且能够执行的方法. 动态方法可以关联到任何已有的模块(Module)中,作为现有模块的扩展. 动态方法可以访问模块中所有类型及其成员.
实现动态方法依赖于:反射发出(System.Reflection.Emit)、 MSIL语言
String.ToLower()方法的MSIL代码
操作指令 |
说明 |
ldarg |
将参数加载到堆栈顶部 例如:ldarg.0 将第一个参数加载到堆栈 |
starg |
将栈顶元素保存到参数 例如:starg.0 将栈顶数据保存到第一个参数 |
ldloc |
将本地变量加载到堆栈顶部 例如:ldloc.0 将第一个变量加载到堆栈 |
stloc |
将栈顶元素保存到本地变量 例如:stloc.0 将栈顶数据保存到第一个变量 |
ldc |
将常量数字加载到堆栈顶部 例如:ldc.i4.2 将2加载到堆栈,i4表示int32数字 |
类似的还有用于加载字符串的ldstr、加载和保存字段的ldfld和stfld、加载数字常量的ldc等等,这些指令执行的操作很好记,以ld开头的就是加载到堆栈,以st开头的就是从堆栈上取回值 | |
call callvirt |
作用都是调用方法,但用法有区别:call用于执行非虚函数或静态方法;callvirt用于执行虚函数或实例方法,注意,这里所说的是一般情况,编译器为了优化也有例外。 |
ret |
方法返回,如果有返回值,则将其从被调者堆栈转移到调用者堆栈顶部 |
nop |
空操作 |
br |
无条件跳转,br.s是br的短格式 例如:br.s L_0002 无条件跳转到L_0002行 |
brtrue |
操作数非空或非False时跳转 例如:brtrue.s L_0002 当操作数为真时跳转到L_0002行 |
brfalse |
操作数为空、为False或为0时跳转 例如:brfalse.s L_0002 当操作数为假时跳转到L_0002行 |
相关的跳转语言还有beq、bge、ble和blt等等,执行跳转语言后,堆栈元素将被清除,即堆栈顶部不包含元素,b和br都是branch的缩写 | |
add |
将两个操作数相加,结果放到堆栈顶部 |
sub |
将两个操作数相减,结果放到堆栈顶部 |
反射发出不是将文本形式的C#代码编译成DLL
{
static void Main(string[] args)
{
Type[] paramTypes = new Type[] { typeof(string) };
DynamicMethod hello = new DynamicMethod("Hello", null, paramTypes, typeof(Program).Module);
ILGenerator ilGen = hello.GetILGenerator();
ilGen.Emit(OpCodes.Ldarg_0);
MethodInfo writeLineMethod = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) });
ilGen.Emit(OpCodes.Call, writeLineMethod);
ilGen.Emit(OpCodes.Ret);
Action<string> invoker = (Action<string>)hello.CreateDelegate(typeof(Action<string>));
invoker("你好,我来自于动态方法");
}
}