2015-04-12 92 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); 
}