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


  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查看输出代码(称为Bat的字符串属性)时,我得到以下信息:

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();
    }
}


请注意最后一行是如何丢失的。

有任何想法吗?

最佳答案

因此,事实证明代码存在一些问题。

首先,正如汉斯·帕桑特(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);
}

关于c# - 使用Reflection.Emit设置属性值,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/29591562/

10-14 20:50
查看更多