所以如果我有:
public class ChildClass : BaseClass
{
public new virtual string TempProperty { get; set; }
}
public class BaseClass
{
public virtual string TempProperty { get; set; }
}
如何使用反射来查看ChildClass隐藏了TempProperty的基本实现?
我希望答案不受C#和VB.NET的影响。
所以如果我有:
public class ChildClass : BaseClass
{
public new virtual string TempProperty { get; set; }
}
public class BaseClass
{
public virtual string TempProperty { get; set; }
}
如何使用反射来查看ChildClass隐藏了TempProperty的基本实现?
我希望答案不受C#和VB.NET的影响。
我们将不得不根据属性的方法来处理,而不是属性本身,因为实际上被覆盖的是属性的get/set方法,而不是属性本身。我将使用get方法,因为永远不应该没有get方法的属性,尽管完整的解决方案应该检查是否缺少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
方法具有相同的标记,而虚基方法与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中,如果两个具有相同签名且都是"hidebysig"的方法分别称为Foo
和foo
,是否可以遮蔽 - 在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;
}
不确定这对于索引属性会如何工作,然而!
更正一下,如果你使用的是VB语言,你要找的属性是"IsHideBySig"。在定义方法/属性时使用了"new"关键字,则此属性值为false。
在C#中,这两种情况都会输出为“hidebysig”。感谢Greg指出这一点。我没有意识到我只测试了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