0

I need to get names of all properties of anonymous type exactly in the order they are declared.

To get them, I can use Type.GetProperties method. It seems to return properties in "correct" order, but according to MSDN I cannot depend on it, because that order might vary.

I also found TypeInfo.DeclaredProperties property which gives me result I expect, but I cannot found any info whether the order is guaranteed or not (at least MSDN doesn't explicitly state it's not guaranteed).

Or is there any other way? It's probably irrelevant, but the anonymous type is actually part of Expression. But I'm not aware of anything in Expression Trees API which can help.

I'm targeting .NET Standard 2.0 and it should work on full .NET framework, .NET Core and Mono/Xamarin.

Sample code:

static void Main(string[] args)
{
    Foo(() => new { Lorem = 1, Ipsum = 2, Dolor = "sit amet" });
}

static void Foo<T>(Expression<Func<T>> expression)
{
    var node = (NewExpression)expression.Body;

    // seems to work, but GetProperties doesn't guarantee order according to MSDN
    var names1 = node.Type.GetProperties().Select(p => p.Name).ToArray();
    // names1 = "Lorem", "Ipsum", "Dolor"

    // seems to work, but is it "bulletproof"?
    var names2 = node.Type.GetTypeInfo().DeclaredProperties.Select(p => p.Name).ToArray();
    // names2 = "Lorem", "Ipsum", "Dolor"
}

Edit: I purposely didn't write exactly why I need it, because I find my specific scenario unnecessarily complicated to explain and wanted to keep the question simple. Long story short - I'm exploring possibilities for micro-ORM API based on parsing expression trees. Not my original case, but here is an example of hypothetical use case where knowing the order is important (I'm not saying such API for sorting is good idea! I just want to know whether something like this is possible):

// translates to SELECT Column1, Column2 FROM Table ORDER BY Column3, Column1
db.From<Table>()
  .Select((x) => new { x.Column1, x.Column2 })
  .OrderBy((x) => new { x.Column3, x.Column1 });
Stalker
  • 678
  • 1
  • 9
  • 21
  • 4
    Obvious question which comes to mind is: why do you need them in that order? Maybe you can avoid relying on that? – Evk Apr 03 '18 at 17:51
  • 3
    I'd *very* strongly advise you to redesign your application to not depend on the order that the properties are declared in. That would just be a nightmare for consumers of the code. – Servy Apr 03 '18 at 17:51
  • You answered your own question with the link you provided: `Your code must not depend on the order in which properties are returned, because that order varies.` – P.Brian.Mackey Apr 03 '18 at 18:38
  • @P.Brian.Mackey The question is asking if there is a *different* way that *can* guarantee order, not whether or not this way guarantees order. – Servy Apr 03 '18 at 18:41
  • I was just being snarky. – P.Brian.Mackey Apr 03 '18 at 18:43
  • Why you need them in order is an important question to answer. If it's for consistent display purposes I would apply your own ordering. `node.Type.GetProperties().OrderBy(p => p.Name)` or something like that. – Wearwolf Apr 03 '18 at 21:36
  • 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:27
  • @Servy says who? If the code is supposed to deal with anonymous types only, then it makes sense. They can be used in generic methods and are often used as templates for methods that expect a bag of properties, such as when it comes to serialization. The syntax is simple and concise, and more importantly it's easy to understand when proper context is given. Any other alternative is exceedingly verbose, either requiring the user to create a custom DTO that will never be used anywhere else in the codebase, or requiring them to provide a lengthy list of metadata-ridden parameters. – KappaG3 Aug 05 '20 at 11:47
  • @KappaG3 Where did I say that anonymous types shouldn't be used? Your comment implies I'm against anonymous types in general, which is just not true. I said that you should design the program so that the order the properties are defined in isn't relied on. – Servy Aug 05 '20 at 12:32
  • @Servy You said that you can't rely on the order of properties not because that's UB, but because _it would be a nightmare for consumers of the code_. What I'm arguing is that if properties were guaranteed to be returned in the same order, it would be extremely helpful when dealing with anonymous types - because you'd be able to use them as ordered sets of properties while exposing a very clean interface... For instance, it would be a great way to define the names and types of the columns of a CSV file... `new{ Name = default(string), Age = default(int) }`... Anon types can't have attributes. – KappaG3 Aug 07 '20 at 09:44
  • So you can't have an [Order] attribute on the properties of an anon type. So you either have to make a custom DTO-like object, or figure out a more verbose way to express the same thing that you could express with an anonymous type very easily if the order of properties were preserved. And my "says who?" is referred to "it would be a nightmare for consumers of the code", because I would be a very happy consumer if I could do this. – KappaG3 Aug 07 '20 at 09:46

2 Answers2

0

For an anonymous type, there is always a constructor that takes all of initial property values as arguments, in the same order they are declared. You can get the position of the constructor argument with the Position property.

See this question to figure out how to get the constructor parameters' names. You can then match those to the type's properties.

public static class ExtensionMethods
{
    public static IEnumerable<PropertyInfo> GetPropertiesInOrder<T>(this T This)
    {
        var type = typeof(T);
        var ctor = type.GetConstructors().Single();
        var parameters = ctor.GetParameters().OrderBy( p => p.Position );
        foreach (var p in parameters)
        {
            yield return type.GetProperty(p.Name);
        }
    }
}

public class Program
{
    public static void Main()
    {
        var o = new { Id = 12, Name="Foo" };
        var list = o.GetPropertiesInOrder();
        foreach (var p in list)
        {
            Console.WriteLine("{0} {1}", p.PropertyType.Name, p.Name);
        }
    }
}

Output:

Int32 Id
String Name

Note: The order of constructor parameters is reliable as it is detailed in the C# language specification (section 7.6.10.6):

An anonymous object initializer declares an anonymous type and returns an instance of that type. An anonymous type is a nameless class type that inherits directly from object. The members of an anonymous type are a sequence of read-only properties inferred from the anonymous object initializer used to create an instance of the type. Specifically, an anonymous object initializer of the form

    new { p1 = e1 , p2 = e2 , … pn = en }

declares an anonymous type of the form

class __Anonymous1
{
    private readonly T1 f1 ;
    private readonly T2 f2 ;
    …
    private readonly Tn fn ;
    public __Anonymous1(T1 a1, T2 a2,…, Tn an) {
        f1 = a1 ;
        f2 = a2 ;
        …
        fn = an ;
    }
    public T1 p1 { get { return f1 ; } }
    public T2 p2 { get { return f2 ; } }
    …
    public T1 p1 { get { return f1 ; } }
    public override bool Equals(object o) { … }
    public override int GetHashCode() { … }
}

where each Tx is the type of the corresponding expression ex.

This specifically defines an anonymous type as a sequence of properties where order is significant and the constructor accepts the initialization data in the same order.

John Wu
  • 50,556
  • 8
  • 44
  • 80
  • 3
    How is relying on one undocumented implementation detail any better than relying on another undocumented implementation detail? – Servy Apr 03 '18 at 17:55
  • 1
    It would be easy to implement without a constructor that takes parameters (i.e. you could do it with property setters not accessible in code), you're relying on the names of those parameters being the same as the properties (not a safe assumption to make), and you're assuming that the order will match the declaration order of the properties (again, not a safe assumption to make). Your code is dramatically *more* likely to break than the OP's code, and worse, can break not only via returning the wrong order, but in even worse ways than that. The OP's code will, at worst, have the wrong order. – Servy Apr 03 '18 at 18:22
  • Your code might not fail either. It could just as easily also be in the wrong order, or have missing properties, say that there are none when they are there or have any number of other possible behaviors. It doesn't just reliably throw if the objects are in the wrong order, it just adds a bunch of *other* dependencies (on entirely unrelated things) that could cause your method to throw *in addition* to having a bunch of silent failure possibilities. I consider that worse, or at best, no better. – Servy Apr 03 '18 at 18:33
  • 1
    The order of the constructor arguments is documented as being `Position`. Whether or not they coincide with the order that the properties are declared is *not*. It's actually not uncommon for an "immutable" object to actually be mutable, but to simply not expose the mutable operations to the code that is expected to observe it as immutable. For example, the `IGrouping` instances returned by the LINQ methods that use that interface actually return mutable types, they just have `internal` methods for mutating them. They just don't mutate it after exposing it publicly. It's a common pattern – Servy Apr 03 '18 at 18:40
  • The order of constructor parameters is guaranteed to match the order declared in the initializer. I've updated my post with supporting material, citing the appropriate section of the c# language specification. – John Wu Apr 03 '18 at 19:02
  • 1
    So the documentation indicates that there is a constructor with the right arguments, but not that the name of the constructor parameters is exactly the same as the property name. – Servy Apr 03 '18 at 19:30
  • Thanks, this seems to be the way. The only problem I have with accepting it as an answer is that I cannot find any confirmation that names of constructor parameters are guaranteed to be the same as names of properties. – Stalker Apr 08 '18 at 15:15
  • Well, if you're really worried about it, you could write a small routine to generate the mapping with certainty; it would have to instantiate a dummy instance, populated with random values, then detect which value ended up where. You could then use that mapping in subsequent calls. Seems a little overkill to me – John Wu Apr 08 '18 at 21:58
0

Based on the example you added to explain why you are doing this... I would like to propose a simple answer, assuming you have an X/Y Problem.

Can you change this (which isn't valid syntax anyway):

// translates to SELECT Column1, Column2 FROM Table ORDER BY Column3, Column1
db.From<Table>()
  .Select((x) => new { x.Column1, x.Column2 })
  .OrderBy((x) => new { x.Column3, x.Column1 });

To something more like this?:

// translates to SELECT Column1, Column2 FROM Table ORDER BY Column3, Column1
db.From<Table>()
  .Select((x) => new [] { x.Column1, x.Column2 })
  .OrderBy((x) => new [] { x.Column3, x.Column1 });

Notice now instead of an anonymous type (where getting the order of properties is a problem) we are using an array (where getting the order is trivial). The syntax is very similar (we just had to add []) but the behavior is much more clearly defined.

John Wu
  • 50,556
  • 8
  • 44
  • 80