2013-06-05 1 views
4

Я новичок в Reflection.Emit и пытаюсь произвести следующие C# код:Reflection.Emit System.InvalidProgramException: Common Language Runtime обнаружена недопустимая программа

public class RepositoryWrapper 
{ 
    public void CallRepositoryMethod(IAddressRepository repository, Address address) 
    { 
     repository.NODE_I_NodeExtendedDetails_Address3(address.NodeId); 
    } 
} 

Вот иль представление о нем :

IL_0000: nop 
    IL_0001: ldarg.1 
    IL_0002: ldarg.2 
    IL_0003: callvirt instance int32 ReflectionServices.Node::get_NodeId() 
    IL_0008: callvirt instance void ReflectionServices.IAddressRepository::NODE_I_NodeExtendedDetails_Address3(int32) 
    IL_000d: nop 
    IL_000e: ret 

И вот мой код, используемый для его создания:

internal static void Generate(this System.Reflection.Emit.ILGenerator @this, Type target,string method,Type instance) 
     { 

     var methodToCall = target.GetMethod(method); 
     var methodParams = methodToCall.GetParameters(); 
     var instanceProperties = instance.GetProperties(BindingFlags.Public | BindingFlags.Instance); 

     var orderedProperties = (from mp in methodParams 
           join p in instanceProperties 
           on mp.Name.ToLower() equals p.Name.ToLower() 
           select p).ToArray(); 

     //add properties to the string builder 

     //load the object reference onto the stack sothat we can access its methods 
     @this.Emit(OpCodes.Nop); 
     @this.Emit(OpCodes.Ldarg_1); 
     @this.Emit(OpCodes.Ldarg_2); 

      var property = orderedProperties.FirstOrDefault(x => x.Name == "NodeId"); 
      if (property != null) 
      { 
       var getMethod = property.GetGetMethod(); 
       @this.Emit(getMethod.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, getMethod); 

      } 


     //call method 
      @this.Emit(methodToCall.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, methodToCall); 
      @this.Emit(OpCodes.Nop); 
     //return from function 
     @this.Emit(OpCodes.Ret); 
     } 

Вот е rror Я получаю:

System.InvalidProgramException: Common Language Runtime detected an invalid program. 
Result StackTrace: 
at ReflectionServices.Repository.NODE_I_NodeExtendedDetails3_Address40807399(Repository  target, Address) 

здесь генерируемый иль:

nop 
ldarg.1 
ldarg.2 
call instance int32 ReflectionServices.Node::get_NodeId() 
callvirt instance void    
ReflectionServices.Repository:: 
           NODE_I_NodeExtendedDetails3_Address(int32) 
nop 

RET

Кто-нибудь может увидеть, что вопрос я застрял?

благодаря

вот моя длл и метод по запросу:

public sealed class ReflectionEmitWithDebuggingMethodGenerator 
{ 
    private AssemblyBuilder Assembly { get; set; } 
    private ModuleBuilder Module { get; set; } 
    private AssemblyName Name { get; set; } 

    public ReflectionEmitWithDebuggingMethodGenerator() 
     : base() 
    { 
     this.Name = new AssemblyName() { Name = Guid.NewGuid().ToString("N") }; 
     this.Assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(
      this.Name, AssemblyBuilderAccess.RunAndSave,@"C:\Users\darren\Documents\Visual Studio 2012\Projects\UnityInjection"); 
     this.AddDebuggingAttribute(this.Assembly); 
     this.Module = this.Assembly.DefineDynamicModule(this.Name.Name + ".dll", true); 
    } 
    public Action<TObject, TInstance> Generate<TObject, TInstance>(Type target, string methodN, Type instanceType) 
    { 
     var type = this.Module.DefineType(target.Namespace + "." + target.Name); 
     var methodName = methodN + target.GetHashCode().ToString(); 
     var method = type.DefineMethod(methodName, MethodAttributes.Static | MethodAttributes.Public, typeof(void), new Type[] { target, instanceType }); 
     method.DefineParameter(1, ParameterAttributes.In, "target"); 
     method.DefineParameter(2, ParameterAttributes.In, "instance"); 

     ILGenerator.Generate(method.GetILGenerator(), target,methodN,instanceType); 

     var createdType = type.CreateType(); 

     var createdMethod = createdType.GetMethod(methodName); 
     return (Action<TObject, TInstance>)Delegate.CreateDelegate(typeof(Action<TObject, TInstance>), createdMethod); 
    } 


} 
+0

Вы получаете сообщение об ошибке при вызове в сгенерированную сборку, не так ли? Вы сравнили сгенерированный IL с IL, который вы моделируете? –

+0

Да, это правильно. Я получаю ошибку при выполнении функции. Я обновил сообщение сгенерированным Il –

+0

. Как вы проверяете сгенерированный ИЛ? Собираете ли вы сборку на диск, а затем открываете что-то вроде Reflector? –

ответ

3

Сравнивая скомпилирован и излучаемый выход, есть только одна разница:

Составитель:

callvirt instance int32 ReflectionServices.Node::get_NodeId() 

Испускаемый:

call instance int32 ReflectionServices.Node::get_NodeId() 

Тип вы звоните int32 get_NodeId() на это ReflectionServices.Node, но тип объекта вы передаете в метод, который вы пытаетесь воспроизвести это Address. Это заставляет меня думать, что свойство accessor, определенное на ReflectionServices.Node, должно быть вызвано практически, возможно, потому, что оно наследуется от другого класса (или реализует интерфейс), который объявляет это свойство до ReflectionServices.Node.

Когда вы излучающие эту строку кода, просто назвать это практически:

@this.Emit(OpCodes.Callvirt, getMethod); 

EDIT: В свете дальнейшего предусмотренного кода, вот реальное решение.

Итак, у вас есть проблемы с основами того, как вы реализуете интерфейс:

var method = type.DefineMethod(methodName, MethodAttributes.Static | Method... 
//               ^^^^^^ 

методы интерфейса не статична; они являются членами экземпляра. Таким образом, сначала необходимо удалить MethodAttributes.Static из флага атрибутов при создании MethodBuilder.

Во-вторых, если вы вернетесь к этой функции, вам нужно будет указать целевой объект , который является экземпляром, на который вызывается метод.Для этого вы можете использовать Activator.CreateInstance, чтобы вызвать созданный по умолчанию конструктор и дать вам экземпляр объекта, который будет использоваться в качестве цели. Замените последнюю строку вашего метода Generate этими строками для достижения этой цели.

var activatedObject = Activator.CreateInstance(type); 

return (Action<TObject, TInstance>)Delegate.CreateDelegate(
    typeof(Action<TObject, TInstance>), activatedObject, createdMethod); 
+0

Вы правы, класс адресов наследуется от узла, у которого есть свойство NodeId. Я попробовал ваше изменение и просто вызвал OpCodes.Callvirt, но он по-прежнему имеет ту же ошибку ... –

+0

Ну, если сгенерированный ИЛ совпадает с чем-то еще, то проблема должна быть проблемой. Можете ли вы изменить свой пост, чтобы включить ваши вызовы в 'DefineType' и' DefineMethod'? –

+0

Хорошо, я добавлю это завтра, спасибо, что посмотрели. –