21

所以如果我有:

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 之间是不可知的

4

4 回答 4

23

我们必须在这里处理属性的方法而不是属性本身,因为实际上被覆盖的是属性的 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 

所以由此我们可以看出,单纯从方法的元数据token中是无法判断是不是new因为non-virtual base方法和non-virtual方法有相同token new,而virtual base方法有相同token作为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 中,如果它们都具有相同的签名并且两者都具有相同的签名,则可以调用一个名为Fooshadow 的方法- 在 C# 中答案是否定的,但如果在 VB 中答案是肯定的,那么它意味着这个问题的答案实际上是不确定的)。foohidebysig

好吧,我不确定这一切有多大帮助,除了说明它实际上是一个比我想象的要困难得多的问题(或者我错过了一些非常明显的事情,在这种情况下我想知道! )。但希望它有足够的内容来帮助你实现你想要做的事情。

于 2008-11-14T00:57:54.467 回答
6

看起来反射不会默认给你这个,所以你必须自己动手:

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;
}

但是,不确定这将如何与索引属性一起使用!

于 2008-11-13T23:13:47.233 回答
0

我从来没有做过你想做的事,但 MethodInfo.GetBaseDefinition() 方法似乎是你要找的。

它返回此方法覆盖的 MethodInfo。

来自 MSDN:

如果使用 new 关键字指定给定方法(如在类型成员中描述的新闻槽中),则返回给定方法。

于 2008-11-13T22:32:15.867 回答
-1

更正,如果您使用的是 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
于 2008-11-13T21:22:58.550 回答