【问题标题】:Get all base property declarations within a class hierarchy获取类层次结构中的所有基本属性声明
【发布时间】:2010-09-22 03:11:19
【问题描述】:

考虑一下这组有趣的类型:

class A     { public virtual     int MyProperty { get; set; } }
class B : A { public override    int MyProperty { get; set; } }
class C : B { public new virtual int MyProperty { get; set; } }
class D : C { public override    int MyProperty { get; set; } }
class E : D { public new         int MyProperty { get; set; } }

我在这里看到三个不同的属性,其中五个实现相互隐藏或覆盖。

我正在尝试获取类型 E 的属性集声明

A.MyProperty
C.MyProperty
E.MyProperty

但我下面的代码给了我一组属性实现

A.MyProperty
B.MyProperty
C.MyProperty
D.MyProperty
E.MyProperty

我需要做什么才能获得属性声明?

或者对于E 的任何实例,B.MyProperty 是否有可能返回 A.MyProperty 以外的值?

如果我的方法朝着错误的方向前进:如何获取一个类型的所有属性成员,包括任何隐藏的成员,但不包括那些永远不会有不同值的成员?


void GetProperties(Type type)
{
    if (type.BaseType != null)
    {
        GetProperties(type.BaseType);
    }

    foreach (var item in type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public))
    {
        Console.WriteLine("{0}.{1}", type.Name, item.Name);
    }
}

期望的输出:

typeof(A) typeof(B) typeof(C) typeof(D) typeof(E) ------------ ------------ ------------ ------------ -- ---------- A.MyProperty A.MyProperty A.MyProperty A.MyProperty A.MyProperty C.我的属性 C.我的属性 C.我的属性 E.MyProperty

【问题讨论】:

  • 我相信这是我第一次看到new virtual. 花了我一秒钟来理解它的意图。
  • 我花了一些时间研究反射对象,才弄清楚如何做到这一点。我认为一旦你弄清楚如何根据这些反射对象来表达这个问题,这个问题就会变得容易得多。弄清楚后,我将问题改写为“列出指定类层次结构中的所有基本属性声明”。

标签: c# .net reflection


【解决方案1】:

这可能会让你走上你想要的道路:

    static void GetProperties(Type type)
    {
        if (type.BaseType != null)
        {
            GetProperties(type.BaseType);
        }

        foreach (var item in type.GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public))
        {
            MethodInfo method = item.GetGetMethod();
            MethodInfo baseMethod = method.GetBaseDefinition();

            System.Diagnostics.Debug.WriteLine(string.Format("{0} {1}.{2} {3}.{4}", type.Name, method.DeclaringType.Name, method.Name, baseMethod.DeclaringType, baseMethod.Name));

            if (baseMethod.DeclaringType == type)
            {
                Console.WriteLine("{0} {1}", type.Name, item.Name);
            }
        }
    }

此代码输出以下内容:

我的属性

C 我的属性

E 我的属性

请注意,此代码依赖于使用与属性关联的 get 方法的 MethodInfo。如果你碰巧有只设置属性,那么你需要做一些额外的检查来处理这种情况。

【讨论】:

  • GetBaseDefinition 是我一直在寻找的关键。谢谢!
【解决方案2】:

但不包括那些永远不会有不同价值的人?

您似乎期望反射系统包含针对这种特殊情况的规则。如果是这样,其他人会抱怨缺少BD 属性。

但我认为答案是:D.MyProperty 是从递归调用中列出的。你知道你已经列出了E.MyProperty,所以看起来没有必要,但是如果你打电话给GetProperties(D)呢?你想省略它吗?

【讨论】:

  • 是的,当我打电话给GetProperties(typeof(D)) 时,我想省略D.MyPropertyE.MyPropertyD.MyProperty 因为它只是覆盖了C.MyProperty,而E.MyProperty 因为DE 一无所知。我已将所有类型的所需输出添加到我的问题中。
【解决方案3】:

每一个都是声明和实现(因为它们都不是抽象的,所以它们都定义了方法体)。关键字 override 和 new 只是提示运行时在给定实例上下文的情况下选择哪个实现。通过反射,您可以绕过正常的继承跟踪并调用特定的实现。

鉴于此,您仍然可以确定其中哪些将是从层次结构中的各个点进行的“根”调用。回想一下,.NET 中的属性对于特定的 getter 和 setter 方法结构和支持字段来说几乎是语法糖,就像访问器是字段本身一样访问。因此,PropertyInfo 暴露了GetGetMethod()GetSetMethod(),这将返回MethodInfo 对象。您应该只需要两者之一,因为它们都将具有赋予属性的继承限定符,但请确保您的属性在层次结构的所有级别都有 get 或 set ,否则这会爆炸。

现在,MethodInfo 公开了几个关于可访问性和继承的属性。对您来说重要的是 IsHideBySig 属性。如果返回 true,则此方法(如在当前类型上声明的)使用 new 关键字在其父级上隐藏相同的签名。因此,在您的代码中,您想查看成员并测试两件事;该类型是否具有除Object 之外的基本类型(或您指定的抽象类型),以及getter 或setter 上的IsHideBySig 是否为真。如果其中任何一个为真,则这是当前类型与具有类似“根”的下一个最派生类型之间的任何类型的“根”实现。

所以,您的 foreach 最终会看起来像这样:

foreach (var item in type.GetProperties(
   BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public)
      .Where(pi=>pi.GetGetMethod().IsHideBySig || pi.ReflectedType.BaseType == typeof(Object))
{
        Console.WriteLine("{0} {1}", type.Name, item.Name);
}

现在请记住,这些“根”不会被调用,除非它们的子级使用 base 标识符显式调用它们,并且无论当前属性是否隐藏或覆盖其父级,都会发生这种情况。不同之处在于当实例被转换为其祖先类型之一时,运行时将调用哪个实现。在这种情况下,选择的实现是转换类型和实际类型之间的继承链中派生最多的类型,从不隐藏其父对象的转换类型开始。如果转换类型的最直接后代隐藏了其父级,则无论该实现对其父级做什么,都会使用该转换类型的实现。

在您的层次结构中,它们是 B、D 和 E。如果您声明一个新的 E 并调用 MyProperty,您将获得 E 的实现。然后,如果您将它传递给采用任何A 并访问MyProperty 的方法,它将使用B 的实现。如果您声明一个 E 并将其视为 C,它将使用 D 的实现。如果您创建了一个 C 并将其视为这样,您将获得 C 的实现(因为 C 不是 D),但如果您将其视为 A,您将获得 B 的实现。

【讨论】:

  • 这听起来很有趣,但代码并没有以当前形式产生预期的结果。
  • 顺便说一句,您完全符合我的要求:获取“根”列表并调用最派生的实现。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-26
  • 1970-01-01
  • 2013-04-13
  • 1970-01-01
  • 1970-01-01
  • 2017-06-11
相关资源
最近更新 更多