所以,如果我有:
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
方法相同的标记,并且虚拟基本方法具有相同的标记作为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; }
但是,不知道这对索引属性如何起作用!