Выполнить метод для постоянной сборки из динамической сборки
Можно ли вызвать метод, который находится на "нормальной" сборке из динамически созданной сборки?
Например, сборка 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, и у меня нет причины для этого.
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);
}
}
Так как CompilerGeneratedMethod
является private
к нему нельзя получить доступ. Поэтому вместо использования лямды давайте попробуем использовать 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);