Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
303 views
in Technique[技术] by (71.8m points)

c# - `Type.GetProperties` property order

Short Version

The MSDN documentation for Type.GetProperties states that the collection it returns is not guaranteed to be in alphabetical or declaration order, though running a simple test shows that in general it is returned in declaration order. Are there specific scenarios that you know of where this is not the case? Beyond that, what is the suggested alternative?

Detailed Version

I realize the MSDN documentation for Type.GetProperties states:

The GetProperties method does not return properties in a particular order, such as alphabetical or declaration order. Your code must not depend on the order in which properties are returned, because that order varies.

so there is no guarantee that the collection returned by the method will be ordered any specific way. Based on some tests, I've found to the contrary that the properties returned appear in the order they're defined in the type.

Example:

class Simple
{
    public int FieldB { get; set; }
    public string FieldA { get; set; }
    public byte FieldC { get; set; }
}
class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Simple Properties:");
        foreach (var propInfo in typeof(Simple).GetProperties())
            Console.WriteLine("{0}", propInfo.Name);
    }
}

Output:

Simple Properties:
        FieldB
        FieldA
        FieldC

One such case that this differs only slightly is when the type in question has a parent who also has properties:

class Parent
{
    public int ParentFieldB { get; set; }
    public string ParentFieldA { get; set; }
    public byte ParentFieldC { get; set; }
}

class Child : Parent
{
    public int ChildFieldB { get; set; }
    public string ChildFieldA { get; set; }
    public byte ChildFieldC { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Parent Properties:");
        foreach (var propInfo in typeof(Parent).GetProperties())
            Console.WriteLine("{0}", propInfo.Name);

        Console.WriteLine("Child Properties:");
        foreach (var propInfo in typeof(Child).GetProperties())
            Console.WriteLine("{0}", propInfo.Name);

    }
}

Output:

Parent Properties:
        ParentFieldB
        ParentFieldA
        ParentFieldC
Child Properties:
        ChildFieldB
        ChildFieldA
        ChildFieldC
        ParentFieldB
        ParentFieldA
        ParentFieldC

Which means the GetProperties method walks up the inheritance chain from bottom up when discovering the properties. That's fine and can be handled as such.

Questions:

  1. Are there specific situations where the described behavior would differ that I've missed?
  2. If depending on the order is not recommended then what is the recommended approach?

One seemingly obvious solution would be to define a custom attribute which indicates the order in which the properties should appear (Similar to the Order property on the DataMember attribute). Something like:

public class PropOrderAttribute : Attribute
{
    public int SeqNbr { get; set; }
}

And then implement such as:

class Simple
{
    [PropOrder(SeqNbr = 0)]
    public int FieldB { get; set; }
    [PropOrder(SeqNbr = 1)]
    public string FieldA { get; set; }
    [PropOrder(SeqNbr = 2)]
    public byte FieldC { get; set; }
}

But as many have found, this becomes a serious maintenance problem if your type has 100 properties and you need to add one between the first 2.

UPDATE

The examples shown here are simply for demonstrative purposes. In my specific scenario, I define a message format using a class, then iterate through the properties of the class and grab their attributes to see how a specific field in the message should be demarshaled. The order of the fields in the message is significant so the order of the properties in my class needs to be significant.

It works currently by just iterating over the return collection from GetProperties, but since the documentation states it is not recommended I was looking to understand why and what other option do I have?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The order simply isn't guaranteed; whatever happens.... Happens.

Obvious cases where it could change:

  • anything that implements ICustomTypeDescriptor
  • anything with a TypeDescriptionProvider

But a more subtle case: partial classes. If a class is split over multiple files, the order of their usage is not defined at all. See Is the "textual order" across partial classes formally defined?

Of course, it isn't defined even for a single (non-partial) definition ;p

But imagine

File 1

partial class Foo {
     public int A {get;set;}
}

File 2

partial class Foo {
    public int B {get;set:}
}

There is no formal declaration order here between A and B. See the linked post to see how it tends to happen, though.


Re your edit; the best approach there is to specify the marshal info separately; a common approach would be to use a custom attribute that takes a numeric order, and decorate the members with that. You can then order based on this number. protobuf-net does something very similar, and frankly I'd suggest using an existing serialization library here:

[ProtoMember(n)]
public int Foo {get;set;}

Where "n" is an integer. In the case of protobuf-net specifically, there is also an API to specify these numbers separately, which is useful when the type is not under your direct control.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...