2015-04-12 61 views
3

我正在构建一个动态代理来拦截我正在编写的库中的一些方法。我可以成功创建我的代理类型,但是当我尝试实现属性设置器时,出现以下错误。使用Reflection.Emit来设置属性值

System.InvalidProgramException

附加信息:

通用语言运行时检测到一个无效的程序。

我发射的代码如下:

public void Emit(FieldInfo interceptorField, 
       MethodInfo method, 
       TypeBuilder typeBuilder) 
{ 
    // Get the method parameters for any setters. 
    ParameterInfo[] parameters = method.GetParameters(); 
    ParameterInfo parameter = parameters.FirstOrDefault(); 

    // Define attributes. 
    const MethodAttributes MethodAttributes = 
      MethodAttributes.Public | MethodAttributes.HideBySig | 
      MethodAttributes.Virtual; 

    // Define the method. 
    MethodBuilder methodBuilder = typeBuilder.DefineMethod(
     method.Name, 
     MethodAttributes, 
     CallingConventions.HasThis, 
     method.ReturnType, 
     parameters.Select(param => param.ParameterType).ToArray()); 

    ILGenerator il = methodBuilder.GetILGenerator(); 

    // Set the correct flags to signal the property is managed 
    // and implemented in intermediate language. 
    methodBuilder.SetImplementationFlags(
     MethodImplAttributes.Managed | MethodImplAttributes.IL); 

    // This is the equivalent to: 
    // IInterceptor interceptor = ((IProxy)this).Interceptor; 
    // if (interceptor == null) 
    // { 
    // throw new NotImplementedException(); 
    // } 
    il.Emit(OpCodes.Ldarg_0); 
    il.Emit(OpCodes.Callvirt, GetInterceptor); 
    Label skipThrow = il.DefineLabel(); 
    il.Emit(OpCodes.Dup); 
    il.Emit(OpCodes.Ldnull); 
    il.Emit(OpCodes.Bne_Un, skipThrow); 
    il.Emit(OpCodes.Newobj, NotImplementedConstructor); 
    il.Emit(OpCodes.Throw); 
    il.MarkLabel(skipThrow); 

    // This is equivalent to: 
    // For get 
    // return interceptor.Intercept(MethodBase.GetCurrentMethod(), null); 
    // For set 
    // interceptor.Intercept(MethodBase.GetCurrentMethod(), value); 
    il.Emit(OpCodes.Call, GetCurrentMethod); 
    il.Emit(parameter == null ? OpCodes.Ldnull : OpCodes.Ldarg_1); 
    il.Emit(OpCodes.Call, InterceptorMethod); 

    if (method.ReturnType != typeof(void)) 
    { 
     il.Emit(OpCodes.Ret); 
    } 
} 

,当输出码看(称为蝙蝠的字符串属性)使用Telerik的JustDecompile我得到如下:

public override void set_Bat(string str) 
{ 
    IInterceptor interceptor = ((IProxy)this).Interceptor; 
    if (interceptor == null) 
    { 
     throw new NotImplementedException(); 
    } 
    interceptor.Intercept(MethodBase.GetCurrentMethod(), str); 
} 

当使用反射器

public override void set_Bat(string str) 
{ 
    IInterceptor interceptor = ((IProxy)this).Interceptor; 
    if (interceptor == null) 
    { 
     throw new NotImplementedException(); 
    } 
} 

注意ho最后一行缺失。

任何想法?

+3

奇怪的代码,你不希望在所有返回时,返回类型为void?首先用C#编写代码,看看它生成的MSIL,然后在Reflection.Emit代码中复制它。 –

+0

您是否尝试将程序集保存到磁盘并在其上运行PEVerify? – svick

+0

另外,'IInterceptor.Intercept()'的返回类型是什么?它是'对象'吗? – svick

回答

0

所以事实证明,代码有一些问题。

首先,Hans Passant指出我在这两种情况下都没有回来。

这是固定使用以下内容。

if (method.ReturnType == typeof(void)) 
{ 
    il.Emit(OpCodes.Pop); 
} 

il.Emit(OpCodes.Ret); 

此外,我打电话MethodBase.GetCurrentMethod()这将无法正常工作。我需要使用MethodBase.GetMethodFromHandle代替,并发出

il.Emit(OpCodes.Ldtoken, method); 
il.Emit(OpCodes.Call, GetMethodFromHandle); 

为了保证MethodInfo上下文正确指向基本类型。

这一切的产量:

public override void set_Bat(string value) 
{ 
    IInterceptor interceptor = this.Interceptor; 
    if (interceptor == null) 
    { 
     throw new NotImplementedException(); 
    } 
    interceptor.Intercept(methodof(Bar.set_Bat), value); 
}