4

I am using the code in https://stackoverflow.com/a/531388/528131 to successfully retrieve all the properties of an object instance from a base, the problem being that the derived type's properties are being iterated over first. Due to the nature of the protocol, i need the base properties first.

x |
y | B
z |
w | A

B and A are classes, B derives from A. x, y, z are properties from B and w is a property from A

This is a scheme of how A.GetProperties(); is returning. I need this instead:

w | A
x |
y | B
z |

Is there any way to get the fields in that order EXACTLY?

Community
  • 1
  • 1
Machinarius
  • 3,637
  • 3
  • 30
  • 53
  • Does this answer your question? [C# Get FieldInfos/PropertyInfos in the original order?](https://stackoverflow.com/questions/5473455/c-sharp-get-fieldinfos-propertyinfos-in-the-original-order) – Ian Kemp Jun 03 '20 at 15:26

6 Answers6

17

The fields in a type are not "ordered". The ordering of the items in those methods is an implementation detail and relying on them is strongly discouraged.

You should order the items yourself, with the expectation that they could start at any position, to ensure your program is robust instead of fragile.

Since each property can be asked for the type that declares it you can create a lookup at the start that gives a number to each class in the hierarchy from the type you start with all the way up to object by walking the BaseType property of Type and order by the lookup value of the declaring type of each property:

public static IEnumerable<PropertyInfo> GetOrderedProperties(Type type)
{
    Dictionary<Type, int> lookup = new Dictionary<Type, int>();

    int count = 0;
    lookup[type] = count++;
    Type parent = type.BaseType;
    while (parent != null)
    {
        lookup[parent] = count;
        count++;
        parent = parent.BaseType;
    }

    return type.GetProperties()
        .OrderByDescending(prop => lookup[prop.DeclaringType]);
}
Servy
  • 202,030
  • 26
  • 332
  • 449
  • I'd need to be able to "slice" `this` into base and derived types, in a hierarchical ladder and ask each slice for THEIR properties, not for derived nor base types' properties. Any ideas? – Machinarius Feb 06 '13 at 16:52
  • This seems to be the easier method. Nice! I will mark as answer when i test it out with a bit more. – Machinarius Feb 06 '13 at 17:20
4

All you have to do is group by declaring type and reverse the list

 var publicProperties = typeof(T).GetProperties()
                .GroupBy(p => p.DeclaringType)
                .Reverse()
                .SelectMany(g => g)
                .ToArray();
Rave
  • 43
  • 1
  • 3
3

The documentation for the reflection subsystem says that you cannot rely on the order in which the elements are returned.

That said, it has always been my experience that elements are returned in order of declaration in the source file. This may or may not be true on Mono or on future revisions of .NET.

Your best option, should you wish to proceed in spite of the above, is to use the BindingFlags.DeclaredOnly option and manually traverse the inheritance hierarchy (scanning base types before subtypes to get them in the right order). You should write your code in such a way that the ordering of properties from a single declared type does not matter (such as, sorting them by name); this will make your code more robust, should the behavior of the .NET framework ever change.

Fasterflect does this (mostly in order to be able to filter out virtual properties that have been overridden). It also has helpers to obtain the properties, filtered or not, using it's own and more powerful Flags selector parameter.

If ordering of the elements within a single type is not important, you could get the list (using Fasterflect) like this:

var properties = type.Properties().Reverse().ToList();

You should be aware that overridden properties will be included multiple times when reflecting this way (by traversing and only getting the declared properties). Fasterflect offers options to filter these from the result:

var properties = type.Properties( Flags.InstancePublic | Flags.ExcludeBackingMembers ).Reverse().ToList();

If you don't wish to take a dependency on the library, the code is open source, so you could pick the bits you need. The traversal algorithm can be seen here (line 443).

Morten Mertner
  • 9,414
  • 4
  • 39
  • 56
  • +1. This will let to select properties in the order specified in the question (need to recursively walk all base classes to get complete list in desired order) – Alexei Levenkov Feb 06 '13 at 16:58
  • 1
    I also assumed properties were always in declared order, because it seemed like they always were when I stepped through code manually. Once I started running unit tests all day long they soon started randomly failing due to using int indexes on properties. Most of the time they are as declared, but every once in a while they are not! When the docs say "not guaranteed" they mean it! I had to abandon my assumptions and re-implement using name indexes. – Etherman Feb 12 '19 at 19:35
2

As an alternative method of sorting:

PropertyInfo[] properties = type.GetProperties(...);
Array.Sort(properties, (pi1, pi2) =>
    {
        if (pi1.DeclaringType.IsSubclassOf(pi2.DeclaringType))
           return 1;
        else if  (pi2.DeclaringType.IsSubclassOf(pi1.DeclaringType))
            return -1;
        else
            return 0;
    });
Rawling
  • 49,248
  • 7
  • 89
  • 127
0

As some people pointed out, relying on the reflection call may be a fragile approach. I'd use additional means to ensure the ordering intentionally, for example by applying an attribute. If you have control over the classes you work on, you may use the pre-existing DataMember attribute on your types. It has a neat Order property which is used to ensure the type fields are serialized in the specified order (some systems may require objects in their communication protocols to respect field oreder, and .NET's data contract serialization achieves this with the help of that attribute field).

However, we may use the semantics ot this to achieve your requirement as well (which could be fine, since your task seems like a form of serialization). You're, of course, free to define a custom attribute class if you feel that the DataMemberAttribute is not OK to be used.

Rougly, I've modified @Rawling's interesting solution like this:

Array.Sort(
    typeof(T).GetProperties(),
    (a, b) =>
    {
        if (a.DeclaringType.IsSubclassOf(b.DeclaringType))
        {
            return -1; // order any property of base class before derived
        }
        else if (b.DeclaringType.IsSubclassOf(a.DeclaringType))
        {
            return 1; // order any property of base class before derived
        }
        else
        {
            // order two properties of the same class 
            // based on order attribute value

            int orderOfA = a.GetCustomAttributes<DataMemberAttribute>().SingleOrDefault()?.Order ?? 0;
            int orderOfB = b.GetCustomAttributes<DataMemberAttribute>().SingleOrDefault()?.Order ?? 0;
            return orderOfB - orderOfA;
        }
    });

If you want to mix base and derived properties in a custom order, you can just use the below snippet, as long as the attribute order values on each property in the base and derived class(es) are chosen with respect of each other:

Array.Sort(
    typeof(T).GetProperties(),
    (a, b) =>
    {
        int orderOfA = a.GetCustomAttributes<DataMemberAttribute>().SingleOrDefault()?.Order ?? 0;
        int orderOfB = b.GetCustomAttributes<DataMemberAttribute>().SingleOrDefault()?.Order ?? 0;
        return orderOfB - orderOfA;
    });
Ivaylo Slavov
  • 8,839
  • 12
  • 65
  • 108
-1

You can get the declaring type from the instance of PropertyInfo and order by its distance from Object.

This is how I would go about it:

void Main()
{
    typeof(B).GetProperties()
    .Select((x,i) => new {
        Prop = x,
        DeclareOrder = i,
        InheritanceOrder = GetDepth(x.DeclaringType),
    })
    .OrderByDescending(x => x.InheritanceOrder)
    .ThenBy(x => x.DeclareOrder)
    .Dump();
}

public class A
{
    public string W {get; set;}
}

public class B : A
{
    public string X {get; set;}
    public string Y {get; set;}
    public string Z {get; set;}
}

static int GetDepth(Type t)
{
    int depth = 0;
    while (t != null)
    {
        depth++;
        t = t.BaseType;
    }
    return depth;
}
Ilia G
  • 10,043
  • 2
  • 40
  • 59
  • 1
    I would like this alot more if you didnt need to declare that getdepth method. – caesay Feb 06 '13 at 17:06
  • 1
    `OrderBy` does a stable sort, rather than an unstable sort, so order of equal items is maintained. Because of that your `ThenBy` is unneeded, as is the select. The only thing you need is `typeof(B).GetProperties().OrderBy(prop => GetDepth(prop))` which has the advantage of just returning the properties since we likely don't care about the depth after the sort. You should probably have an extra `Select` to get the property out in your case. Note that this approach is more or less what I did in my answer, except that I pre-calcuated the depths and cached them to avoid repetition. – Servy Feb 06 '13 at 17:08
  • @caesay Well, you don't, per say, but it's most certainly better with the method. Why are you opposed to creating helper methods? – Servy Feb 06 '13 at 17:09
  • This technique does not work as expected with inheritance. It put my base class properties at the end of the list. – Greg Mulvihill Sep 23 '16 at 18:23