jeudi 23 août 2018

Implementing an interface and forwarding method calls at runtime using Reflection.Emit

I need to implement an interface at runtime.

The implementation of all methods should just call some common static method (like 'Executor.Execute()') while passing the MethodInfo of the interface method being implemented, and forwarding any arguments passed to the newly emitted implementation.

The code works so far, and prints "Hello" when a method is called on the generated implementation.

Now, instead of WriteLine("Hello"), I need to insert code to call 'Executor.Execute()' with the MethodInfo of the interface method, etc..

How do I pass the 'method' variable from the for loop and any arguments of the implemented method to Executor.Execute?

public class Executor
{
    public static object Execute(MethodInfo method, object[] args)
    {
        Console.WriteLine($"Executing {method.Name}");
        return null;
    }
}

public class Builder
{
    public static T Implement<T>() where T : class
    {
        var interfaceType = typeof(T);

        var assemblyName = new AssemblyName(Guid.NewGuid().ToString());
        var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
        var moduleBuilder = assemblyBuilder.DefineDynamicModule("Implementation");

        var typeBuilder = moduleBuilder.DefineType("Implementation",
                                                   TypeAttributes.Public |
                                                   TypeAttributes.Class |
                                                   TypeAttributes.AutoClass |
                                                   TypeAttributes.UnicodeClass |
                                                   TypeAttributes.AutoLayout,
                                                   null);

        typeBuilder.AddInterfaceImplementation(interfaceType);

        // Implement all interface methods as calls to Executor.Execute()
        // passing the MethodInfo of the interface method being implemented,
        // and the arguments passed to the implemented method.
        foreach(var method in interfaceType.GetMethods())
        {
            var methodBuilder = typeBuilder.DefineMethod(
                method.Name,
                MethodAttributes.Public | MethodAttributes.Virtual,
                method.ReturnType,
                method.GetParameters().Select(p => p.GetType()).ToArray());

            var ilGen = methodBuilder.GetILGenerator();
            ilGen.EmitWriteLine("Hello");
            ilGen.Emit(OpCodes.Ret);

            // Generate a call like this:
            // -first arg is the 'method' from this for loop
            // -second is args passed to the generated method
            // Executor.Execute(method, args...);

            typeBuilder.DefineMethodOverride(methodBuilder, interfaceType.GetMethod(method.Name));
        }

        return Activator.CreateInstance(typeBuilder.CreateType()) as T;
    }
}

public interface Test {
    void Hello();
}

class Program
{
    static void Main(string[] args) {
        var impl = Builder.Implement<Test>();
        impl.Hello(); // should print "Executing Hello"
    }
}





Aucun commentaire:

Enregistrer un commentaire