在虚方法上使用 OpCodes.Call 安全吗?
Is it safe to use OpCodes.Call on a virtual method?
我正在尝试为属性生成动态代理。
生成的代理源自我们要代理的类型。
当代理需要访问其派生类型上的(虚拟)属性 时,无法使用 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);
不使用 declaringTypeMethod 而是使用 thisTypeMethod 是否安全?
您通常不需要声明类型的实现。
您可能想做与 base
关键字对 C# 编译器所做的相同的事情。 C#编译器实际上是查找最派生的父实现并直接调用它,但你这样做也是完全合法的。
如果基础 类 在另一个程序集中,它们会有不同的行为,并且在代码生成运行后重新编译该程序集并添加新的覆盖。有关更多详细信息,请参阅 Eric Lippert(C# 编译器主要开发人员之一)的博客 post,其中解决了这个 exact 场景:
这个问题说明了 OpCodes.Call
针对当前方法与具有实际实现的最派生父方法之间的行为差异:
- Method binding to base method in external library can't handle new virtual methods "between"
重申一下,您不想使用 DeclaringType
中的实现,这通常不是上述两个合理选择中的任何一个。
我正在尝试为属性生成动态代理。
生成的代理源自我们要代理的类型。
当代理需要访问其派生类型上的(虚拟)属性 时,无法使用 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);
不使用 declaringTypeMethod 而是使用 thisTypeMethod 是否安全?
您通常不需要声明类型的实现。
您可能想做与 base
关键字对 C# 编译器所做的相同的事情。 C#编译器实际上是查找最派生的父实现并直接调用它,但你这样做也是完全合法的。
如果基础 类 在另一个程序集中,它们会有不同的行为,并且在代码生成运行后重新编译该程序集并添加新的覆盖。有关更多详细信息,请参阅 Eric Lippert(C# 编译器主要开发人员之一)的博客 post,其中解决了这个 exact 场景:
这个问题说明了 OpCodes.Call
针对当前方法与具有实际实现的最派生父方法之间的行为差异:
- Method binding to base method in external library can't handle new virtual methods "between"
重申一下,您不想使用 DeclaringType
中的实现,这通常不是上述两个合理选择中的任何一个。