所以如果我有:
public class ChildClass : BaseClass
{
public new virtual string TempProperty { get; set; }
}
public class BaseClass
{
public virtual string TempProperty { get; set; }
}
如何使用反射来查看 ChildClass 隐藏了 TempProperty 的 Base 实现?
我希望答案在 c# 和 vb.net 之间是不可知的
我们必须在这里处理属性的方法而不是属性本身,因为实际上被重写的是属性的 get/set 方法而不是属性本身。我将使用 get 方法,因为您永远不应该拥有没有属性的属性,尽管完整的解决方案应该检查是否缺少属性。
查看在许多情况下发出的 IL,基本属性的“get”方法将具有元数据标记(这是来自 C# 编译器;其他可能不会发出
hidebysig
,具体取决于其方法隐藏语义,在在这种情况下,该方法将按名称隐藏):
non-virtual : .method public hidebysig specialname instance
virtual : .method public hidebysig specialname newslot virtual instance
衍生的代币将具有以下代币:
override : .method public hidebysig specialname virtual instance
new : .method public hidebysig specialname instance
new virtual : .method public hidebysig specialname newslot virtual instance
因此我们可以看出,单纯从方法的元数据标记中无法判断它是否为
new
,因为非虚拟基方法与非虚拟 new
方法具有相同的标记,并且virtual base 方法与 new virtual
方法具有相同的标记。
我们可以说的是,如果该方法具有
virtual
标记但没有 newslot
标记,那么它会覆盖基本方法而不是隐藏它,即
var prop = typeof(ChildClass).GetProperty("TempProperty");
var getMethod = prop.GetGetMethod();
if ((getMethod.Attributes & MethodAttributes.Virtual) != 0 &&
(getMethod.Attributes & MethodAttributes.NewSlot) == 0)
{
// the property's 'get' method is an override
}
假设我们发现“get”方法不是重写,我们想知道基类中是否有一个属性被它隐藏。问题在于,由于该方法位于不同的方法表槽中,因此它实际上与它所隐藏的方法没有任何直接关系。所以我们实际上说的是“基类型是否有任何满足隐藏标准的方法”,这取决于该方法是
hidebysig
还是按名称隐藏。
对于前者,我们需要检查基类是否有与签名完全匹配的方法,而对于后者,我们需要检查它是否有同名的方法,所以继续上面的代码:
else
{
if (getMethod.IsHideBySig)
{
var flags = getMethod.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic;
flags |= getMethod.IsStatic ? BindingFlags.Static : BindingFlags.Instance;
var paramTypes = getMethod.GetParameters().Select(p => p.ParameterType).ToArray();
if (getMethod.DeclaringType.BaseType.GetMethod(getMethod.Name, flags, null, paramTypes, null) != null)
{
// the property's 'get' method shadows by signature
}
}
else
{
var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance;
if (getMethod.DeclaringType.BaseType.GetMethods(flags).Any(m => m.Name == getMethod.Name))
{
// the property's 'get' method shadows by name
}
}
}
我认为这就是大部分的方式,但我仍然不认为这是完全正确的。首先,我不太熟悉按名称隐藏,因为 C# 不支持它,而这几乎是我使用的所有内容,因此我在此处的代码中可能是错误的,指示实例方法可能会隐藏静态方法。我也不知道区分大小写的问题(例如,在 VB 中,如果一个名为
Foo
的方法具有相同的签名并且都是 foo
,那么它们是否可以隐藏一个名为 hidebysig
的方法 - 在 C# 中,答案是否定的,但是如果在 VB 中答案是肯定的,那么这意味着这个问题的答案实际上是不确定的)。
嗯,我不确定这一切有多大帮助,除了说明这实际上是一个比我想象的更难的问题(或者我错过了一些非常明显的东西,在这种情况下我想知道!)。但希望它有足够的内容来帮助您实现您想要做的事情。
看起来反射默认不会给你这个,所以你必须自己动手:
public static bool IsHidingMember( this PropertyInfo self )
{
Type baseType = self.DeclaringType.BaseType;
PropertyInfo baseProperty = baseType.GetProperty( self.Name, self.PropertyType );
if ( baseProperty == null )
{
return false;
}
if ( baseProperty.DeclaringType == self.DeclaringType )
{
return false;
}
var baseMethodDefinition = baseProperty.GetGetMethod().GetBaseDefinition();
var thisMethodDefinition = self.GetGetMethod().GetBaseDefinition();
return baseMethodDefinition.DeclaringType != thisMethodDefinition.DeclaringType;
}
但是不确定这将如何与索引属性一起使用!
我从来没有做过你想做的事情,但 MethodInfo.GetBaseDefinition() 方法似乎就是你正在寻找的。
它返回该方法正在重写的 MethodInfo。
来自 MSDN :
如果使用 new 关键字指定给定方法(如类型成员中所述的新闻槽),则返回给定方法。
我知道这已经很旧了,但这就是我解决这个问题的方法,它满足了我的需求:
public static bool IsHiddenByType(this PropertyInfo property, Type type) =>
property.DeclaringType != type &&
type.GetProperties()
.Where(p => p.DeclaringType == type)
.Select(p=>p.Name)
.Contains(property.Name);
更正,如果您使用 VB,您要查找的属性是“IsHideBySig”。 如果使用“new”关键字来定义方法/属性,则这将是错误的。
在 C# 情况下,两个实例都输出为“hidebysig”。 感谢您指出格雷格。 我没有意识到我只在 VB 中测试过这个。 下面是重现此行为的示例 VB 代码。
Module Module1
Class Foo
Public Function SomeFunc() As Integer
Return 42
End Function
End Class
Class Bar
Inherits Foo
Public Shadows Function SomeFunc() As Integer
Return 36
End Function
End Class
Sub Main()
Dim type = GetType(Bar)
Dim func = type.GetMethod("SomeFunc")
Stop
End Sub
End Module