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