如何使用Reflection.emit在运行时创建方法

时间:2010-08-23 20:34:09

标签: c#-4.0 reflection.emit

我在运行时使用反射发射创建一个对象。我成功创建了字段,属性和获取设置方法。 现在我想添加一个方法。为简单起见,我们假设该方法只返回一个随机数。如何定义方法体?

编辑:

是的,我一直在查看msdn文档以及其他参考文献,我开始对这些东西感到满意。 我看到上面的例子是如何添加和/或多重,但是如果我的方法正在做其他事情该怎么办。我该如何定义“东西” 假设我是动态生成下面的类,我将如何创建GetDetails()方法的主体?

class TestClass
{
    public string Name  { get; set; }
    public int Size  { get; set; }

    public TestClass()
    {
    }

    public TestClass(string Name, int Size)
    {
        this.Name = Name;
        this.Size = Size;
    }

    public string GetDetails()
    {
        string Details = "Name = " + this.Name + ", Size = " + this.Size.ToString();
        return Details;
    }
}

1 个答案:

答案 0 :(得分:20)

您使用MethodBuilder来定义方法。要定义方法体,请调用GetILGenerator()以获取ILGenerator,然后调用Emit方法以发出单独的IL指令。 MethodBuilder的MSDN文档中有一个示例,您可以在Using Reflection Emit页面上找到有关如何使用反射发射的其他示例:

public static void AddMethodDynamically(TypeBuilder myTypeBld,
                                    string mthdName,
                                    Type[] mthdParams,
                                    Type returnType,
                                    string mthdAction)
{
    MethodBuilder myMthdBld = myTypeBld.DefineMethod(
                                            mthdName,
                                            MethodAttributes.Public |
                                            MethodAttributes.Static,
                                            returnType,
                                            mthdParams);
    ILGenerator ILout = myMthdBld.GetILGenerator();
    int numParams = mthdParams.Length;
    for (byte x = 0; x < numParams; x++)
    {
        ILout.Emit(OpCodes.Ldarg_S, x);
    }
    if (numParams > 1)
    {
        for (int y = 0; y < (numParams - 1); y++)
        {
            switch (mthdAction)
            {
                case "A": ILout.Emit(OpCodes.Add);
                    break;
                case "M": ILout.Emit(OpCodes.Mul);
                    break;
                default: ILout.Emit(OpCodes.Add);
                    break;
            }
        }
    }
    ILout.Emit(OpCodes.Ret);
}

听起来你正在寻找编写MSIL的资源。一个重要的资源是OpCodes类,它有每个IL指令的成员。文档描述了每条指令的工作原理。另一个重要资源是IldasmReflector。这些将让您看到编译代码的IL,这将帮助您了解您想要编写的IL。通过Reflector运行GetDetailsMethod并将语言设置为IL会产生:

.method public hidebysig instance string GetDetails() cil managed
{
    .maxstack 4
    .locals init (
        [0] string Details,
        [1] string CS$1$0000,
        [2] int32 CS$0$0001)
    L_0000: nop 
    L_0001: ldstr "Name = "
    L_0006: ldarg.0 
    L_0007: call instance string ConsoleApplication1.TestClass::get_Name()
    L_000c: ldstr ", Size = "
    L_0011: ldarg.0 
    L_0012: call instance int32 ConsoleApplication1.TestClass::get_Size()
    L_0017: stloc.2 
    L_0018: ldloca.s CS$0$0001
    L_001a: call instance string [mscorlib]System.Int32::ToString()
    L_001f: call string [mscorlib]System.String::Concat(string, string, string, string)
    L_0024: stloc.0 
    L_0025: ldloc.0 
    L_0026: stloc.1 
    L_0027: br.s L_0029
    L_0029: ldloc.1 
    L_002a: ret 
}

要动态生成类似的方法,您需要为每条指令调用ILGenerator.Emit:

ilGen.Emit(OpCodes.Nop);
ilGen.Emit(OpCodes.Ldstr, "Name = ");
ilGen.Emit(OpCodes.Ldarg_0);
ilGen.Emit(OpCodes.Call, nameProperty.GetGetMethod());
// etc..

您可能还想查找MSIL的介绍,例如:Introduction to IL Assembly Language