从动态程序集执行持久化程序集的方法

时间:2014-10-10 16:46:33

标签: c# .net reflection.emit

可以从动态构造的程序集中调用驻留在“Normal”程序集上的方法吗?

例如,程序集B是从程序集A动态构造的(通过Emit),程序集B需要调用在程序集A上定义的静态方法。

public interface IMapper
{
  void Map();
}

public void CreateDynamic() {
  AppDomain app = AppDomain.CurrentDomain;
  AssemblyName name = new AssemblyName("assemblyB");
  AssemblyBuilder assembly = app
    .DefineDynamicAssembly(name, AssemblyBuilderAccess.Run);
  ModuleBuilder module = assembly
    .DefineDynamicModule(assembly.GetName().Name, "b.dll");

  TypeBuilder type = module.DefineType("MyType",
    TypeAttributes.Public |
      TypeAttributes.Class |
      TypeAttributes.AutoClass |
      TypeAttributes.AutoLayout, null, new[] {typeof (IMapper)});

  MethodBuilder method = type
    .DefineMethod("Map",
      MethodAttributes.Public |
        MethodAttributes.HideBySig |
        MethodAttributes.Virtual);

  ILGenerator il = method.GetILGenerator();

  Func<int, TimeSpan> func = i => TimeSpan.FromSeconds(i);

  il.Emit(OpCodes.Ldc_I4_S, 10);
  il.Emit(OpCodes.Callvirt, func.Method);
  il.Emit(OpCodes.Ret);

  Type t = type.CreateType();

  IMapper mapper = (IMapper) Activator.CreateInstance(t);
  mapper.Map();
}

执行Map方法时会抛出MissingMethodExcetion,我现在不知道原因。

1 个答案:

答案 0 :(得分:1)

下面的行导致在编译时在类中创建private static方法。

Func<int, TimeSpan> func = i => TimeSpan.FromSeconds(i);

所以你要做的就是这样。

public class TestClass {
    private static TimeSpan CompilerGeneratedMethod(int i) {
        return TimeSpan.FromSeconds(i);
    }

    public void CreateDynamic() {
        // Other codes...

        var methodInfo = typeof(TestClass).GetMethod("CompilerGeneratedMethod", BindingFlags.Static | BindingFlags.NonPublic);

        il.Emit(OpCodes.Ldc_I4_S, 10);
        il.Emit(OpCodes.Callvirt, methodInfo);
        il.Emit(OpCodes.Ret);

        // Other codes...
    }
}

现在我们刚刚在AssemblyB中创建了一个试图在另一个程序集中调用private static方法的类。如果它直接用C#编写,它可能看起来像这样。

public class MyType : IMapper {
    public void Map() {
        TestClass.CompilerGeneratedMethod(10);
    }
}

由于CompilerGeneratedMethodprivate,因此无法访问。因此,不要使用lamda,而是尝试使用真正声明的public方法。

public class TestClass {
    public static TimeSpan HandWrittenMethod(int i) {
        return TimeSpan.FromSeconds(i);
    }

    public void CreateDynamic() {
        // Other codes...

        var methodInfo = typeof(TestClass).GetMethod("HandWrittenMethod");

        il.Emit(OpCodes.Ldc_I4_S, 10);
        il.Emit(OpCodes.Callvirt, methodInfo);
        il.Emit(OpCodes.Ret);

        // Other codes...
    }
}

现在我们有一个小问题。我们尝试使用Callvirt调用静态方法,该方法应该用于后期绑定方法。我们应该只使用Callvirt而不是Call

 il.Emit(OpCodes.Call, methodInfo);