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

本文关键字:OpCodes Call 是否 安全 虚拟 方法 | 更新日期: 2023-09-27 17:57:23

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

生成的代理派生自我们要代理的类型。当代理需要访问其派生类型的(虚拟)属性时,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; } 
    }
}

当我在从MyChild派生的代理对象上发出OpCodes.Call来调用get_Color它会被正确调用,即使从技术上讲,此方法未在MyChild上实现。

我打算编写一些代码来遍历类型层次结构,以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);

不使用声明类型方法并改用此类型方法是否安全?

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

您通常不需要声明类型的实现。

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

如果基类位于另一个程序集中,并且在代码生成运行后重新编译该程序集添加新的重写,则它们具有不同的行为。 有关更多详细信息,请参阅 Eric Lippert(C# 编译器主要开发人员之一)的这篇博客文章,其中解决了以下确切情况:

  • 在中间放一个底座

此问题说明了针对当前方法的OpCodes.Call与具有实际实现的最派生父级之间的行为差异:

  • 与外部库中的基方法绑定的方法无法在"之间"处理新的虚拟方法

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