在虚拟方法上使用OpCodes.Call是否安全?

时间:2015-06-22 04:39:12

标签: c# .net reflection reflection.emit

我正在为属性生成动态代理。

生成的代理是从我们想要代理的类型派生的。 当代理需要访问它所导出的类型的(虚拟)属性时,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?

1 个答案:

答案 0 :(得分:1)

您通常不希望从声明类型实施。

据推测,您希望执行base关键字与C#编译器相同的操作。 C#编译器实际上查找最派生的父实现并直接调用它,但你正在做的事情也完全合法。

如果基类位于另一个程序集中,它们会有不同的行为,并且在代码生成运行后重新编译该程序集并添加新的替代。有关更多详细信息,请参阅Eric Lippert(C#编译器主要开发人员之一)的这篇博客文章,其中介绍了完全方案:

此问题说明了OpCodes.Call与当前方法之间的行为差​​异,以及具有实际实现的派生程度最高的父级:

重申一下,您不希望使用DeclaringType中的实现,这通常不是上述两种合理选择中的任何一种。