0

I need to get all values of all properties of a class element/item that has been previously populated with data. Class example:

public class MyClass
{
    public string prop1 { get; set; }
    public string prop2 { get; set; }
    public string prop3 { get; set; }
    public string prop4 { get; set; }
}

Somewhere in my code I have:

MyClass myClass = new MyClass();
List<MyClass> myList = new List<MyClass>();

MyClass myElement = new MyClass()
{
    prop1 = "A",
    prop2 = "B",
    prop3 = "C",
    prop4 = "D",
};
myList.Add(myElement);

string wholeString_opt1 = 
    myElement.prop1 + "\t" + 
    myElement.prop2 + "\t" +
    myElement.prop3 + "\t" + 
    myElement.prop4 + "\t";

In my case the order in which I add all property values is fundamental (from first property to the last property it must have that sequence). The example shows a simple unique element list. The properties of my class are very similar (all start with prop) but in many cases the names are not alphabetically ordered in the class. This means that, if I have a considerable amount of properties in my class with different starting chars, the result of the 'wholeString_opt1' will be right if I sort the properties but it is very labor intensive.

An alternative would be something like:

PropertyInfo[] myClassProperties = typeof(MyClass).GetProperties();
foreach (PropertyInfo property in myClassProperties)
{
    wholeString_opt2 += property.GetValue(myElement).ToString() + "\t";
}

Where 'wholeString_opt2' would have the same result but only if the properties of the class are, as shown above. If we have, e.g.:

MyClass myElement = new MyClass()
{
    prop1 = "A",
    prop2 = "B",
    anotherProp3 = "C",
    prop4 = "D",
};

The sorting of the .GetProperties() will be lost.

Is there any way we can overcome this problem? I might have cases with more than 25 properties... Maybe there is even other options?

Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
ptuga
  • 69
  • 1
  • 11
  • 2
    Do you mean order of _declaration_ or order of _initialization_? – SLaks Dec 11 '18 at 22:10
  • 2
    You are not guaranteed to go through in order of deceleration. You would need to store the order as metadata that you want to iterate with. – gh9 Dec 11 '18 at 22:11
  • 2
    If you rely on the compiler to compile the code in the same order each time you might find that a future update to the framework changes things around. You need to use attributes on the properties to define the order so that the order is immutable. – Enigmativity Dec 11 '18 at 22:15
  • The order I need to consider (because I don't know other way) is the order of the properties declaration (prop1 ... prop4). Still, open to any viable way to do this. – ptuga Dec 11 '18 at 22:35
  • What does it means "use attributes on the properties" and how can I use them to make sure the order/sequence is always the expected? – ptuga Dec 11 '18 at 22:36
  • @TheGeneral one simple example would be useful. Thanks – ptuga Dec 11 '18 at 22:58
  • 1
    Would it be an option to add a `OutputString` function in `MyClass` that returns the data as you see fit? Then at least only one place needs to care about the order of the data (the class itself, rather than the consumer). – mjwills Dec 11 '18 at 23:46
  • MetadataToken should be good, I haven't yet heard of anybody getting stung by it. – Hans Passant Dec 12 '18 at 00:01

1 Answers1

1

I think you can use Attributes here as suggested by TheGeneral.

For example, given the following PostionAttribute:

    [AttributeUsage(AttributeTargets.Property)]
    public sealed class PositionAttribute : Attribute
    {
        public PositionAttribute(int position)
        {
            Position = position;
        }

        public int Position { get; private set; }
    }

We can specify the order of each property in MyClass as we like, such as:

public class MyClass
    {
        [Position(1)]
        public string prop1 { get; set; }

        [Position(2)]
        public string prop2 { get; set; }

        [Position(3)]
        public string anotherProp3 { get; set; }            

        [Position(4)]
        public string prop4 { get; set; }
    }

To test the above changes, please try:

        var myClass = new MyClass();
        var myList = new List<MyClass>();

        var myElement = new MyClass()
         {
              prop1 = "A",
              prop2 = "B",
              anotherProp3 = "C",
              prop4 = "D",
         };

       var values = typeof(MyClass).GetProperties()
                    .OrderBy(p => Attribute.IsDefined(p, typeof(PositionAttribute)) ? p.GetCustomAttribute<PositionAttribute>().Position : int.MaxValue)
                    .Select(p => p.GetValue(myElement))
                    .ToArray();

       var result = string.Join("\t", values);

The output will be "A B C D".

Another good solution can be found from here.

Hope it helps.

Ping
  • 124
  • 3
  • 2
    The comment by @mjwills points to a better attribute that uses `CallerLineNumber` automatically. – NetMage Dec 12 '18 at 00:13
  • Yes, as suggested in my post, CallerLineNumber provides a good solution as well. However, one advantage of the PositionAttribute is it gives us a little bit more flexibility to set specific orders for certain properties. – Ping Dec 12 '18 at 00:40
  • @NetMage: This solution is very helpful and easy if you would like to change the order of the properties at a later date. The index position may not be relevant and if that's the case you don't have to worry about changing the index on the attributes. – ptuga Dec 21 '18 at 19:02
  • 1
    @ptuga Depending on the order of declaration is easy, self-documenting and removes possibilities for errors. Manually declaring the position allows for errors such as skipped positions when properties are removed (must renumber), properties are added in the middle (must renumber), duplicate positions (should have renumbered), out of order properties (how do you know?). There's a reason we don't use line numbers in modern programming languages for statement order. – NetMage Dec 21 '18 at 19:36