使用 Reflection.Emit 设置 属性 值
Using Reflection.Emit to set a property value
我正在构建一个动态代理来拦截我正在编写的库中的某些方法。我可以成功创建我的代理类型,但是当我尝试实现 属性 setter 时,出现以下错误。
System.InvalidProgramException
Addition Information:
Common Language Runtime detected an invalid program.
我的Emitter代码如下:
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);
}
使用 Reflector 时
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);
}
我正在构建一个动态代理来拦截我正在编写的库中的某些方法。我可以成功创建我的代理类型,但是当我尝试实现 属性 setter 时,出现以下错误。
System.InvalidProgramException
Addition Information:
Common Language Runtime detected an invalid program.
我的Emitter代码如下:
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);
}
使用 Reflector 时
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);
}