views:

382

answers:

3

Declaring a property in a derived class that matches the name of a property in the base class "hides" it (unless it overrides it with the override keyword). Both the base and derived class properties will be returned by Type.GetProperties() if their types don't match. However, if their types do match, shockingly only the derived class's property is returned. For instance:

class A
{
    protected double p;
    public int P { get { return (int)p; } set { p = value; } }
}
class B : A
{
    public new int P { get { return (int)p; } set { p = value; } }
}
class C : B
{
    public new float P { get { return (float)p; } set { p = value; } }
}

Calling typeof(C).GetProperties() will only return B.P and C.P. Is it possible to call GetProperties() in a way that returns all three? There is almost certainly a way to do it by traversing the inheritance hierarchy, but is there a cleaner solution?

+5  A: 

GetProperties is defined as all public properties of the type. You could get their get and set methods using typeof(C).GetMethods().Where(m => m.Name.StartsWith("set_") || m.Name.StartsWith("get_")). Although this seems like a bad idea, compared to going down the inheritance hierarchy to get the properties.

Yuriy Faktorovich
Then you would have the same problem with the get_P method hiding the one of the parent, I think.
driis
@Yuriy: typeof(C).GetMethods().Where@driis: No, it seems to work properly.
Eric Mickelsen
I just tried it. I get all 6.
Yuriy Faktorovich
You are right. This method also works. Not quite sure why it works, since I assume the same method hiding rules go for methods as well as properties. Hmm.
driis
This would work for me if I could get at the PropertyInfo from the getter or setter.
Eric Mickelsen
What do you need from the PropertyInfo that is not provided by the getter and setter?
Yuriy Faktorovich
I suppose it would work without the PropertyInfo, but I would have to be able to effectively match each getter with its respective setter, and that seems messy.
Eric Mickelsen
It's not quite clean, but this is the *only* method so far that really returns exactly the right number of properties. Therefore, it is my accepted answer. It it still just amazing to me that reflection can work so well will fields and methods, but fall flat on its face with properties.
Eric Mickelsen
+5  A: 

I do not think it is possible without traversing the inheritance hierarchy. It does not have to be too much code, though:

    public static IEnumerable<PropertyInfo> GetAllProperties(Type t)
    {
        while (t != typeof(object))
        {
            foreach (var prop in t.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance))
                yield return prop;
            t = t.BaseType;
        }
    }

Of course, if you know a common basetype you can stop at, instead of object, it will be more efficient. Also; it will take some time to do the reflection, so cache the result. After all, the type information won't change during execution.

driis
Won't this yield overridden properties multiple times?
Eric Mickelsen
No, because he said BindingFlags.DeclaredOnly.
Yuriy Faktorovich
The problem here is that if we re-write our class hierarchy so that B.P overrides A.P, we still get three properties even though an object of type C now only has two.
Eric Mickelsen
In contrast, Yuriy, your answer would yield only two sets of get/setters, which is the correct behavior.
Eric Mickelsen
You're right, I misread what you said. I also tested my method and it does return only two.
Yuriy Faktorovich
+1  A: 

Through reflection, the new keyword only hides the inherited property if the signature matches. I guess reflection matches signatures on property accessors (get_ & set_). It's the reasons why GetProperties() returns B.P and C.P when the return type differ.

I recently discovered Fasteflect which provides advanced reflection mechanisms.

I checked and Fasteflect type.Properties returns all the tree of hidden members (P). I think the API consider backing members (virtual/override) and hidden members (new) differently which is a good thing for your 'problem' ;)

My test with fasterflect :

class Class1
{
    public object field1 = null;

    public virtual object Property1 { get; set; }

    public object Property2 { get; set; }

    public string Property3 { get; set; }
}

class Class2 : Class1
{
    public new object field1 = null;

    public override object Property1 { get; set; }

    public new string Property3 { get; set; }
}

class Class3 : Class2
{
    public new string Property3 { get; set; }
}

Filter backing members but returns all hidden members :

typeof(Class3).Properties(Flags.ExcludeBackingMembers | Flags.Public | Flags.Instance) 
  • typeof(Class3).Properties(Flags.ExcludeBackingMembers | Flags.Public | Flags.Instance) Count = 5 System.Collections.Generic.IList
    • [0] {System.String Property3} System.Reflection.PropertyInfo
    • [1] {System.Object Property1} System.Reflection.PropertyInfo
    • [2] {System.String Property3} System.Reflection.PropertyInfo
    • [3] {System.Object Property2} System.Reflection.PropertyInfo
    • [4] {System.String Property3} System.Reflection.PropertyInfo
JoeBilly
http://msdn.microsoft.com/en-us/library/51y09td4%28VS.71%29.aspx#vclrfnew_newmodifier : A constant, field, property, or type introduced in a class or struct hides all base class members with the same name.
Eric Mickelsen
JoeBilly
After some testing and inspection of Fasterflect, I can safely say that their answer to this problem is totally equivalent to driis's. Their recursive solution to finding hidden members is incapable of excluding virtual properties that have been overridden. (Actually, setting the ExcludeBackingMembers flag somehow excludes all three properties in both cases.) However, thanks for the link.
Eric Mickelsen
Weird, I added my test in the post. I tested again and ExcludeBackingMembers don't exclude hidden members. I tried without auto properties too : same behavior.
JoeBilly
Interesting - it did work much better with exactly the flags you used. However, it still failed in my testing. If you hide an overridden property, it still disappears. (Add public new object Property1 to your Class3.) So, in effect, there are still properties there that can be used, but are not visible to reflection.
Eric Mickelsen
JoeBilly