Emit 辅助类
由于动态编译是基于 Emit 的,因此在定义构造函数、方法和属性时,都要使用到 IL(中间语言),在 DynamicMethod 实例或上下文 Context 中可以使用 EmitHelper 对象来编织 IL 代码。它是对 ILGenerator 类的全部操作的包装,以达到在使用 IL 指令时更加方便,具体的方法请参考 OpCodes 类的常量定义。为了保持与 IL 指令一致,EmitHelper 类的方法使用小写命名。
1、IL指令包装
与指令相关的方法或属性如下(举例):
add 属性:将两个值相加并将结果推送到计算堆栈上。
add_ovf 属性:将两个整数相加,执行溢出检查,并且将结果推送到计算堆栈上。
add_ovf_un 属性:将两个无符号整数值相加,执行溢出检查,并且将结果推送到计算堆栈上。
and 属性:计算两个值的按位“与”并将结果推送到计算堆栈上。
beq_s 方法:如果两个值相等,则将控制转移到目标指令(短格式)。
ldarg_0 属性:将索引为 0 的参数加载到计算堆栈上。
ldc_i4_ 方法:将指定的整数作为 int32 推送到计算堆栈上。
ldloc 方法:将指定索引处的局部变量加载到计算堆栈上。
callvirt 方法:对对象调用后期绑定方法,并且将返回值推送到计算堆栈上。
unbox 方法:将值类型的已装箱的表示形式转换为其未装箱的形式。
。。。。。。
最后表一列出了所有 IL 指令对应的方法或属性。
2、辅助方法
- ldarg 方法
将索引处的参数加载到堆栈上。IL 指令最多支持 ldarg_1、ldarg_2、ldarg_3,和 ldarg_s 方法,但使用起来不太简便,因此 ldarg(int index) 方法为他们提供了统一的调用。
- end 方法
方法本身没有具体的意义,仅在 EmitHelper 对象在使用了属性之后,再使用 end 方法以表结束。
3、其他方法
- Assert 方法
Assert 方法判断一个断言是否成立,如果成立则进行相应的链式编织。如下所示:
[TestMethod]
public void TestEmitAssert()
{
var callMethod = true; //用于模拟断言
var builder = new DynamicMethod("TestHello", typeof(bool), null);
var emiter = new EmitHelper(method.GetILGenerator())
.Assert(callMethod,
e => e.ldstr("fireasy")
.call(typeof(Helper).GetMethod("GetHello", new[] { typeof(string) })),
e => e.ldstr("hello fireasy"))
.call(typeof(Console).GetMethod("WriteLine", new[] { typeof(string) }))
.ldc_i4_1.ret();
var func = builder.CreateDelegate(typeof(Func<bool>));
Assert.IsTrue((bool)func.DynamicInvoke(null));
}
public class Helper
{
public static string GetHello(string name)
{
return "hello " + name;
}
}
//------方法原型--------
public static bool TestHello()
{
Console.WriteLine(Helper.GetHello("fireasy"));
return true;
}
//----------------------
- Each 方法
Each 方法用于遍列一个序列,将序列中的元素编辑到 EmitHelper 对象里。如下所示:
[TestMethod]
public void TestEmitEach()
{
var cities = new[] { "kunming", "chengdu", "guangzhou" };
var builder = new DynamicMethod("TestItems", typeof(int), new[] { typeof(List<string>) });
var emiter = new EmitHelper(method.GetILGenerator())
.Each(cities, (e, str, i) =>
{
e.ldarg_0.ldstr(str)
.callvirt(typeof(List<string>).GetMethod("Add"));
})
.ldarg_0.callvirt(typeof(List<string>).GetMethod("get_Count")).ret();
var items = new List<string>();
var func = builder.CreateDelegate(typeof(Func<List<string>, int>));
Assert.AreEqual(3, (int)func.DynamicInvoke(items));
}
//------方法原型--------
public static int TestItems(List<string> items)
{
items.Add("kunming");
items.Add("chengdu");
items.Add("guangzhou");
return items.Count;
}
//----------------------
- For 方法
For 方法用于循环一组数字,一般用在循环体内有共性的指令之时,如下所示:
[TestMethod]
public void TestEmitFor()
{
var method = new DynamicMethod("TestWrite", typeof(void),
new[] { typeof(string), typeof(string), typeof(int) });
var parameters = method.GetParameters();
var emiter = new EmitHelper(method.GetILGenerator())
.ldc_i4(parameters.Length)
.newarr(typeof(object))
.For(0, parameters.Length, (e, i) =>
{
e.dup.ldc_i4(i).ldarg(i)
.Assert(parameters[i].ParameterType.IsValueType,
e1 => e1.box(parameters[i].ParameterType))
.stelem_ref.end();
})
.call(typeof(string).GetMethod("Concat", new[] { typeof(object[]) }))
.call(typeof(Console).GetMethod("WriteLine", new[] { typeof(string) }))
.ret();
var action = method.CreateDelegate(typeof(Action<string, string, int>));
action.DynamicInvoke("fireasy", "kunming", 33);
}
//------方法原型--------
public static void TestWrite(string name, string city, int age)
{
Console.WriteLine(string.Concat(new object[] { name, city, age }));
}
//----------------------
4、表一:IL指令与方法/属性对应表
| 指令 | 方法 / 属性 |
|---|---|
| Add | P add |
| Add.Ovf | P add_ovf |
| Add.Ovf.Un | P add_ovf_un |
| And | P and |
| Arglist | P arglist |
| Beq | M beq |
| Beq.S | M beq_s |
| Bge | M bge |
| Bge.S | M bge_s |
| Bge.Un | M bge_un |
| Bge.Un.S | M bge_un_s |
| Bgt | M bgt |
| Bgt.S | M bgt_s |
| Bgt.Un | M bgt_un |
| Bgt.Un.S | M bgt_un_s |
| Ble | M ble |
| Ble.S | M ble_s |
| Ble.Un | M ble_un |
| Ble.Un.S | M ble_un_s |
| Blt | M blt |
| Blt.S | M blt_s |
| Blt.Un | M blt_un |
| Blt.Un.S | M blt_un_s |
| Bne.Un | M bne_un |
| Bne.Un.S | M bne_un_s |
| Box | M box |
| Br | M br |
| Br.S | M br_s |
| Break | P break |
| Brfalse | M brfalse |
| Brfalse.S | M brfalse_s |
| Brtrue | M brtrue |
| Brtrue.S | M brtrue_s |
| Call | M call |
| Calli | M calli |
| Callvirt | M callvirt |
| Castclass | M castclass |
| Ceq | P ceq |
| Cgt | P cgt |
| Cgt.Un | P cgt_un |
| Ckfinite | P ckfinite |
| Clt | P clt |
| Clt.Un | P clt_un |
| Constrained | M constrained |
| Conv.I | P conv_i |
| Conv.I1 | P conv_i1 |
| Conv.I2 | P conv_i2 |
| Conv.I4 | P conv_i4 |
| Conv.I8 | P conv_i8 |
| Conv.Ovf.I | P conv_ovf_i |
| Conv.Ovf.I.Un | P conv_ovf_i_un |
| Conv.Ovf.I1 | P conv_ovf_i1 |
| Conv.Ovf.I1.Un | P conv_ovf_i1_un |
| Conv.Ovf.I2 | P conv_ovf_i2 |
| Conv.Ovf.I2.Un | P conv_ovf_i2_un |
| Conv.Ovf.I4 | P conv_ovf_i4 |
| Conv.Ovf.I4.Un | P conv_ovf_i4_un |
| Conv.Ovf.I8 | P conv_ovf_i8 |
| Conv.Ovf.I8.Un | P conv_ovf_i9_un |
| Conv.Ovf.U | P conv_ovf_u |
| Conv.Ovf.U.Un | P conv_ovf_u_un |
| Conv.Ovf.U1 | P conv_ovf_u1 |
| Conv.Ovf.U1.Un | P conv_ovf_u1_un |
| Conv.Ovf.U2 | P conv_ovf_u2 |
| Conv.Ovf.U2.Un | P conv_ovf_u2_un |
| Conv.Ovf.U4 | P conv_ovf_u4 |
| Conv.Ovf.U4.Un | P conv_ovf_u4_un |
| Conv.Ovf.U8 | P conv_ovf_u8 |
| Conv.Ovf.U8.Un | P conv_ovf_u8_un |
| Conv.R.Un | P conv_r_un |
| Conv.R4 | P conv_r4 |
| Conv.R8 | P conv_r8 |
| Conv.U | P conv_u |
| Conv.U1 | P conv_u1 |
| Conv.U2 | P conv_u2 |
| Conv.U4 | P conv_u4 |
| Conv.U8 | P conv_u8 |
| Cpblk | P cpblk |
| Cpobj | M cpobj |
| Div | P div |
| Div.Un | P div_un |
| Dup | P dup |
| Endfilter | P endfilter |
| Endfinally | Pendfinally |
| Initblk | P initblk |
| Initobj | M initobj |
| Isinst | M isinst |
| Jmp | M jmp |
| Ldarg | M ldarg |
| Ldarg.0 | P ldarg_0 |
| Ldarg.1 | P ldarg_1 |
| Ldarg.2 | P ldarg_2 |
| Ldarg.3 | P ldarg_3 |
| Ldarg.S | M ldarg_s |
| Ldarga | P ldarga |
| Ldarga.S | P ldarga_s |
| Ldc.I4 | M ldc_i4 |
| Ldc.I4.0 | P ldc_i4_0 |
| Ldc.I4.1 | P ldc_i4_1 |
| Ldc.I4.2 | P ldc_i4_2 |
| Ldc.I4.3 | P ldc_i4_3 |
| Ldc.I4.4 | P ldc_i4_4 |
| Ldc.I4.5 | P ldc_i4_5 |
| Ldc.I4.6 | P ldc_i4_6 |
| Ldc.I4.7 | P ldc_i4_7 |
| Ldc.I4.8 | P ldc_i4_8 |
| Ldc.I4.M1 | P ldc_i4_m1 |
| Ldc.I4.S | M ldc_i4_s |
| Ldc.I8 | M ldc_i8 |
| Ldc.R4 | M ldc_r4 |
| Ldc.R8 | M ldc_r8 |
| Ldelem | M ldelem |
| Ldelem.I | P ldelem_i |
| Ldelem.I1 | P ldelem_i1 |
| Ldelem.I2 | P ldelem_i2 |
| Ldelem.I4 | P ldelem_i4 |
| Ldelem.I8 | P ldelem_i8 |
| Ldelem.R4 | P ldelem_r4 |
| Ldelem.R8 | P ldelem_r8 |
| Ldelem.Ref | P ldelem_ref |
| Ldelem.U1 | P ldelem_u1 |
| Ldelem.U2 | P ldelem_u2 |
| Ldelem.U4 | P ldelem_u4 |
| Ldelema | M ldelema |
| Ldfld | M ldfld |
| Ldflda | M ldflda |
| Ldftn | M ldftn |
| Ldind.I | P ldind_i |
| Ldind.I1 | P ldind_ii |
| Ldind.I2 | P ldind_i2 |
| Ldind.I4 | P ldind_i4 |
| Ldind.I8 | P ldind_i8 |
| Ldind.R4 | P ldind_r4 |
| Ldind.R8 | P ldind_r8 |
| Ldind.Ref | P ldind_ref |
| Ldind.U1 | P ldind_u1 |
| Ldind.U2 | P ldind_u2 |
| Ldind.U4 | P ldind_u4 |
| Ldlen | P ldlen |
| Ldloc | M ldloc |
| Ldloc.0 | P ldloc_0 |
| Ldloc.1 | P ldloc_1 |
| Ldloc.2 | P ldloc_2 |
| Ldloc.3 | P ldloc_3 |
| Ldloc.S | P ldloc_s |
| Ldloca | M ldloca |
| Ldloca.S | M ldloc_s |
| Ldnull | P ldnull |
| Ldobj | M ldobj |
| Ldsfld | M ldsfld |
| Ldsflda | M ldsflda |
| Ldstr | M ldstr |
| Ldtoken | M ldtoken |
| Ldvirtftn | M ldvirtftn |
| Leave | M leave |
| Leave.S | M leave_s |
| Localloc | P localloc |
| Mkrefany | M mkrefany |
| Mul | P mul |
| Mul.Ovf | P mul_ovf |
| Mul.Ovf.Un | P mul_ovf_un |
| Neg | P neg |
| Newarr | M newarr |
| Newobj | M newobj |
| Nop | P nop |
| Not | P not |
| Or | P or |
| Pop | P pop |
| Prefix1 | - |
| Prefix2 | - |
| Prefix3 | - |
| Prefix4 | - |
| Prefix5 | - |
| Prefix6 | - |
| Prefix7 | - |
| Prefixref | - |
| Readonly | P readonly |
| Refanytype | P refanytype |
| Refanyval | M refanyval |
| Rem | P rem |
| Rem.Un | P rem_un |
| Ret | M ret |
| Rethrow | P rethrow |
| Shl | P shl |
| Shr | P shr |
| Shr.Un | P shr_un |
| Sizeof | M sizeof |
| Starg | M starg |
| Starg.S | M starg_s |
| Stelem | M stelem |
| Stelem.I | P stelem_i |
| Stelem.I1 | P stelem_i1 |
| Stelem.I2 | P stelem_i2 |
| Stelem.I4 | P stelem_i4 |
| Stelem.I8 | P stelem_i8 |
| Stelem.R4 | P stelem_r4 |
| Stelem.R8 | P stelem_r8 |
| Stelem.Ref | P stelem_ref |
| Stfld | M stfld |
| Stind.I | P stind_i |
| Stind.I1 | P stind_i1 |
| Stind.I2 | P stind_i2 |
| Stind.I4 | P stind_i4 |
| Stind.I8 | P stind_i8 |
| Stind.R4 | P stind_r4 |
| Stind.R8 | P stind_r8 |
| Stind.Ref | P stind_ref |
| Stloc | M stloc |
| Stloc.0 | P stloc_0 |
| Stloc.1 | P stloc_1 |
| Stloc.2 | P stloc_2 |
| Stloc.3 | P stloc_3 |
| Stloc.S | P stloc_s |
| Stobj | M stobj |
| Stsfld | M stsfld |
| Sub | P sub |
| Sub.Ovf | P sub_ovf |
| Sub.Ovf.Un | P sub_ovf_un |
| Switch | M switch |
| Tailcall | P tailcall |
| Throw | P throw |
| Unaligned | M unaligned |
| Unbox | M unbox |
| Unbox.Any | M unbox_any |
| Volatile | P volatile |
| Xor | P xor |