我正在为属性生成动态代理。
生成的代理是从我们想要代理的类型派生的。
当代理需要访问它所导出的类型的(虚拟)属性时,OpCodes.Callvirt
无法使用 - 它会导致无限递归。因此,我们需要致电OpCodes.Call
。我注意到,如果我有:
public class MyParent
{
protected string _name;
protected string _color;
public virtual string Name
{
get { return _name; }
set { _name = value; }
}
public virtual string Color
{
get { return _color; }
set { _color = value; }
}
}
public class MyChild : MyParent
{
public override string Name {
get { return "42"; }
set { _name = value; }
}
}
当我从OpCodes.Call
派生的代理对象上发出MyChild
来调用get_Color
时,即使从技术上讲,此方法未在MyChild
上实现,也会正确调用它。 / p>
我打算编写一些遍历类型层次结构的代码,直到MyParent
可以找到get_Color
实现,并使用OpCodes.Call
的类型方法,但看起来这不是必要的:
var thisTypeMethod = property.GetGetMethod();
// I know that the next line technically is not correct because of non-virtual methods
// and also *new* overrides. Assume I'm doing it correctly, not by property.Name
// but by repeatedly calling MethodInfo.GetBaseDefinition()
var declaringTypeMethod = property.DeclaringType.GetProperty(property.Name).GetGetMethod();
然后
var proxyMethod = new DynamicMethod(thisTypeMethod.Name,thisTypeMethod.ReturnType, new Type[]{type},type,true);
var il = proxyMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Tailcall);
il.Emit(OpCodes.Call, thisTypeMethod);
il.Emit(OpCodes.Ret);
是否安全不使用declaringTypeMethod并改为使用thisTypeMethod?
答案 0 :(得分:1)
您通常不希望从声明类型实施。
据推测,您希望执行base
关键字与C#编译器相同的操作。 C#编译器实际上查找最派生的父实现并直接调用它,但你正在做的事情也完全合法。
如果基类位于另一个程序集中,它们会有不同的行为,并且在代码生成运行后重新编译该程序集并添加新的替代。有关更多详细信息,请参阅Eric Lippert(C#编译器主要开发人员之一)的这篇博客文章,其中介绍了完全方案:
此问题说明了OpCodes.Call
与当前方法之间的行为差异,以及具有实际实现的派生程度最高的父级:
重申一下,您不希望使用DeclaringType
中的实现,这通常不是上述两种合理选择中的任何一种。