考虑这组有趣的类型:
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
但是下面的代码为我提供了一组属性 implementation :
A.MyProperty
B.MyProperty
C.MyProperty
D.MyProperty
E.MyProperty
获取属性声明需要做什么?
或B.MyProperty
是否有可能为A.MyProperty
的任何实例返回E
以外的值?
如果我的方法朝着错误的方向前进:我如何获得一个类型的所有属性成员,包括任何隐藏的属性,但不包括那些永远不会有不同值的属性?
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.MyProperty C.MyProperty C.MyProperty E.MyProperty
答案 0 :(得分:2)
这可能会让你开始沿着你想要的道路前进:
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);
}
}
}
此代码输出以下内容:
MyProperty
C MyProperty
E MyProperty
请注意,此代码依赖于使用与属性关联的get方法的MethodInfo。如果你碰巧只有set属性,那么你需要做一些额外的检查来处理这种情况。
答案 1 :(得分:1)
但不包括永远不会有不同价值的那些?
您似乎期望Reflection系统包含针对此特定情况的规则。如果是这样,其他人会抱怨B
和D
属性丢失了。
但我认为答案是:D.MyProperty
是从递归调用中列出的。您知道自己已经列出了E.MyProperty
所以它似乎没有用,但如果您拨打GetProperties(D)
该怎么办?你想要省略吗?
答案 2 :(得分:0)
这些都是声明和实现(因为它们都不是抽象的,所以它们都定义了方法体)。关键字覆盖和new只是提示运行时关于给定实例的上下文选择哪个实现的提示。通过反射,您可以绕过正常的继承跟踪并调用特定的实现。
鉴于此,您仍然可以确定哪些是从层次结构中的各个点进行的“根”调用。回想一下,.NET中的属性对于特定的getter和setter方法结构以及支持字段来说几乎是语法糖,访问就像访问者是字段本身一样。因此,PropertyInfo
会公开GetGetMethod()
和GetSetMethod()
,它们将返回MethodInfo
个对象。您应该只需要两个中的一个,因为它们都具有赋予属性的继承限定符,但是请确保您的属性在层次结构的所有级别都有get或set,否则会爆炸。
现在,MethodInfo
公开了几个有关可访问性和继承的属性。对您来说重要的是IsHideBySig
属性。如果返回true,则此方法(在当前类型上声明)使用new
关键字在其父级上隐藏相同的签名。因此,在您的代码中,您希望查看成员,并测试两件事;类型是否具有除Object
之外的基本类型(或您指定的抽象类型),以及getter或setter上的IsHideBySig
是否为true。如果其中任何一个为真,则这是当前类型与具有类似“根”的下一个派生类型之间的任何类型的“根”实现。
所以,你的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的实现。