Выполнить метод для постоянной сборки из динамической сборки

1

Можно вызвать метод, который находится на "обычной" сборке из динамически построенной сборки?

Например, сборка B динамически построена (через Emit) из сборки A, а сборка 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, и теперь я не являюсь причиной этого.

Теги:
reflection.emit

1 ответ

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 метод в другой сборке. Если он был написан непосредственно на С#, он может выглядеть примерно так.

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

Поскольку CompilerGeneratedMethod является private доступ к нему невозможен. Поэтому, вместо использования 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);
  • 0
    Я не могу создать настоящий общедоступный метод, поскольку средство отображения предназначено для использования внешними библиотеками, поэтому имя метода не известно заранее.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню