c# - 当属性隐藏具有 'new' 关键字的继承成员时,反射如何告诉我?

标签 c# vb.net reflection inheritance overriding

如果我有:

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 还是 hide-by-name。

对于前者,我们需要检查基类是否有任何与签名完全匹配的方法,而对于后者,我们需要检查它是否有任何同名方法,所以继续上面的代码:

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 中答案是肯定的那么这意味着这个问题的答案实际上是不确定的。

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

关于c# - 当属性隐藏具有 'new' 关键字的继承成员时,反射如何告诉我?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/288357/

相关文章:

vb.net - 显示文本框工具提示

sql - 使用 Access 存储用户名和密码并通过 Visual Basic 打开

c# - 通过特定属性过滤类实例的属性并调用属性的方法

golang反射初始化满足接口(interface)的结构

reflection - 打印出 Groovy 对象中的变量和值

c# - DataGridView 在添加行时抛出 "InvalidOperationException: Operation is not valid..."

c# - 派生类导致 InsertOnSubmit() 抛出 NullReferenceException

c# - 有没有办法强制 NHTMLUNIT 忽略页面 JavaScript 错误并继续执行脚本?

.net - AppDomain 仅部分尊重InitializeLifetimeService(如果有的话)

c# - Windows 服务与 Windows 窗体在同一进程中