简体   繁体   中英

Invoking method of AutoGenerated class with Reflection.Emit

Hello i was wondering how can you invoke a method of a class that has not come into existence yet without the use of reflexion.As you can see i am using a generator class Weaver (that uses Reflection.Emit ) that returns a Func<object> (i can not specify the dynamic type).When i retrieve the instance ,how would i call one of its method ,specifically DoInt ?

What i want to AutoGenerate

class Gen {
            public Gen() {

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

Generator class

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);
            }

        }

Main

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();
        }

To make it work, you first need to fix your Weaver , as it contains several bugs:

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;
}

Now there is several ways to call A.DoInt:

  1. Reflection :

     var c = Weaver.Weave(); var result = (int)c.GetType().GetMethod("DoInt").Invoke(c, new Object[] { 3, "foo" }); 
  2. Create delegate :

     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. Dynamic :

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

    This one is the most difficult to do. First you must declare interface such as:

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

    You need to declare this interface in another assembly, otherwise you would not be able to use this type from dynamically defined assembly (due to circular reference).

    Then you need to change Weaver a bit, to make your A type implement this interface:

     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")); } 

    Now you can call DoInt on IDoInt interface:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM