首页 > 解决方案 > 使用 Reflection.Emit 调用 AutoGenerated 类的方法

问题描述

您好,我想知道如何在不使用反射的情况下调用尚未存在的类的方法。如您所见,我正在使用返回 a 的生成器类Weaver(使用Reflection.EmitFunc<object>(我无法指定动态类型)。当我检索实例时,我将如何调用它的方法之一,特别是DoInt

我想自动生成什么

class Gen {
            public Gen() {

            }
            public int DoInt(int a,string b) {
               int rez=a+b.count();
                return 3;
            }
        }

生成器类

class Weaver {

            public static Func<object> Weave() {
                var weaver = new Weaver();
                weaver.Run();
                return weaver.output as Func<object>;

            }


            private AssemblyBuilder assemblyBuilder;
            private ModuleBuilder moduleBuilder;
            private TypeBuilder typebuilder;

            private object output;
            public Weaver() {

            }


            private void Run() {
                this.DefineAssembly();
                this.DefineModule();
                this.DefineClass();
                this.DefineMethod();
                this.Wrap();
            }


            private void DefineAssembly() {
                AssemblyName name = new AssemblyName("Coda");
                AppDomain domain = AppDomain.CurrentDomain;
                assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndCollect);
            }

            private void DefineModule() {
                this.moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyBuilder.FullName + ".dll");
            }
            private void DefineClass() {
                this.typebuilder = this.moduleBuilder.DefineType("A", 
                    TypeAttributes.Abstract|TypeAttributes.Public|TypeAttributes.BeforeFieldInit|TypeAttributes.AnsiClass|TypeAttributes.AutoClass,
                    typeof(object), null );
            }
            private void DefineMethod() {
                MethodBuilder methodBuilder = this.typebuilder.DefineMethod("DoInt", MethodAttributes.Public, typeof(int), new[] { typeof(int), typeof(string) });
                var il = methodBuilder.GetILGenerator();
                il.Emit(OpCodes.Ldarg_1);
                il.EmitCall(OpCodes.Call, typeof(Enumerable).GetMethod("Count", BindingFlags.Static), null);
                il.Emit(OpCodes.Ldarg_1);
                il.Emit(OpCodes.Add);
                il.Emit(OpCodes.Ret);
            }

            private void Wrap() {
                Type type = typebuilder.CreateType();
                this.output = Activator.CreateInstance(type);
            }

        }

主要的

static void Main(string[] args)
        {
            string t = "astada";
            var c = Weaver.Weave();
            var result=c.GetType().GetMethod("DoInt").Invoke(null, new object[] { 3, "mystring" });  //should be 3+ "mystring".Count() 
            Console.ReadLine();
        }

标签: c#reflection.emit

解决方案


为了让它工作,你首先需要修复你的Weaver,因为它包含几个错误:

private void DefineClass()
{
    // TypeAttributes.Abstract should not be used here as we want to create
    // type that can be instantiated
    this.typebuilder = this.moduleBuilder.DefineType(
        "A",
        TypeAttributes.Public | TypeAttributes.BeforeFieldInit | TypeAttributes.AnsiClass | TypeAttributes.AutoClass,
        typeof(object),
        null);
}

private void DefineMethod()
{
    MethodBuilder methodBuilder = this.typebuilder.DefineMethod(
        "DoInt",
        MethodAttributes.Public,
        typeof(int), new[] { typeof(int), typeof(string) });

    var il = methodBuilder.GetILGenerator();

    // Arguments are counted from zero. For instance methods, argument0 is
    // reserved for 'this' instance. So to get "string" argument (second "real" argument),
    // you need Ldarg_2
    il.Emit(OpCodes.Ldarg_2);

    // You cannot get MethodInfo for "Count" method with simple GetMethod(),
    // since it is generic method with several overloads.
    var countMethodInfo = typeof(Enumerable)
        .GetMethods(BindingFlags.Public | BindingFlags.Static)
        .Where(m => m.Name == "Count")
        .Where(m => m.GetParameters().Length == 1)
        .Single()
        //We want Count<char>() method, because we want to count characters on string (casted to IEnumerable<char>).
        .MakeGenericMethod(typeof(char));
    il.EmitCall(OpCodes.Call, countMethodInfo, null);
    il.Emit(OpCodes.Ldarg_1);
    il.Emit(OpCodes.Add);
    il.Emit(OpCodes.Ret);
}

public static object Weave()
{
    var weaver = new Weaver();
    weaver.Run();
    // return weaver.output as Func<object>;
    // Output is an instance of an (dynamically generated) 'A' class, not a Func<>
    return weaver.output;
}

现在有几种方法可以调用 A.DoInt:

  1. 反射

    var c = Weaver.Weave();
    var result = (int)c.GetType().GetMethod("DoInt").Invoke(c, new Object[] { 3, "foo" });
    
  2. 创建委托

    var c = Weaver.Weave();
    var mi = c.GetType().GetMethod("DoInt");
    var del = (Func<int, string, int>)Delegate.CreateDelegate(typeof(Func<int, string, int>), c, mi);
    var result = del(3, "foo");
    
  3. 动态

    var d = Weaver.Weave() as dynamic;
    var result = (int)d.DoInt(3, "foo");
    
  4. 接口

    这是最难做到的。首先,您必须声明接口,例如:

    public interface IDoInt
    {
        int DoInt(int i, string s);
    }
    

    您需要在另一个程序集中声明此接口,否则您将无法从动态定义的程序集中使用此类型(由于循环引用)。

    然后你需要做一些改变Weaver,让你的A类型实现这个接口:

    private void DefineClass()
    {
        this.typebuilder = this.moduleBuilder.DefineType(
            "A",
            TypeAttributes.Public | TypeAttributes.BeforeFieldInit | TypeAttributes.AnsiClass | TypeAttributes.AutoClass,
            typeof(object),
            new[] { typeof(IDoInt) });
    }
    
    private void DefineMethod()
    {
        MethodBuilder methodBuilder = this.typebuilder.DefineMethod(
            "DoInt",
            MethodAttributes.Public | MethodAttributes.HideBySig |
            MethodAttributes.NewSlot |  MethodAttributes.Virtual |
            MethodAttributes.Final,
            typeof(int), new[] { typeof(int), typeof(string) });
    
        // ... rest ot the method is the same ...
    
        // just add this at the end of the method to implement the IDoInt interface
        this.typebuilder.DefineMethodOverride(methodBuilder, typeof(IDoInt).GetMethod("DoInt"));
    }
    

    DoInt现在您可以调用IDoInt接口:

    var i = Weaver.Weave() as IDoInt;
    var result = i.DoInt(3, "foo");
    

推荐阅读