2015-06-22 72 views
2

我正在为属性生成动态代理。在虚拟方法上使用OpCodes.Call是否安全?

生成的代理来自我们想要代理的类型。 当代理需要访问从其派生而来的类型的(虚拟)属性时,不能使用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.CallMyChild导出的代理对象上调用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); 

它是否安全不使用declaringTypeMethod并使用thisTypeMethod来代替?

回答

1

你通常不希望从声明类型的实现。

想必您也会做同样的事情,base关键字会用C#编译器做。 C#编译器实际上查找了派生得最多的父实现并直接调用它,但是你所做的也是完全合法的。

如果基类在另一个程序集中,并且该程序集重新编译并在代码生成运行后添加新的覆盖,它们会有不同的行为。欲了解更多详情,请参阅通过埃里克利珀这个博客帖子(的C#编译器的主要开发者之一),它解决了这个确切场景:

这个问题说明了两者之间的行为差​​异OpCodes.Call逆水方法,并与实际的实现最衍生母体:

重申一下,您不希望使用​​中的实现,该实现通常不是上述两个合理选择之一。