46

Imagine a class with many public properties. For some reason, it is impossible to refactor this class into smaller subclasses.

I'd like to add a ToString override that returns something along the lines of:

Property 1: Value of property 1\n
Property 2: Value of property 2\n
...

Is there a way to do this?

mafu
  • 31,798
  • 42
  • 154
  • 247

5 Answers5

109

I think you can use a little reflection here. Take a look at Type.GetProperties().

public override string ToString()
{
    return GetType().GetProperties()
        .Select(info => (info.Name, Value: info.GetValue(this, null) ?? "(null)"))
        .Aggregate(
            new StringBuilder(),
            (sb, pair) => sb.AppendLine($"{pair.Name}: {pair.Value}"),
            sb => sb.ToString());
}
Oliver
  • 43,366
  • 8
  • 94
  • 151
  • 3
    +1 Nice answer. There is a Minor defect. _PropertyInfos is always null it should be `_PropertyInfos = this.GetType().GetProperties();` in the if. – Conrad Frix Oct 30 '10 at 15:53
  • @conrad-frix: Yes, you're right. Fixed it, but due to the mark as correct answer and the upvotes i think everyone fixed it already on himself. – Oliver Nov 02 '10 at 08:13
  • Thanks for this answer. btw it does throw exceptions on null property values. – Alex Rice Apr 25 '14 at 12:19
  • 3
    @AlexRice: Yes, you're right. I think, everybody who ran into this problem fixed it by himself. But having a copy&paste solution that also considered this, makes it easier for everyone. So i changed the code to take care for the null values. – Oliver Apr 28 '14 at 06:13
  • 1
    Any reason you are naming with an `_` and a Capital letter? e.g. `_PropertyInfos`? Seems to go against every coding convention ever – Kellen Stuart Mar 05 '20 at 20:10
  • 1
    The nice thing about conventions is that they are not rules nor laws and that there are so many of them. – Oliver Mar 06 '20 at 06:34
  • 2
    Just an update here that you don't really have to cache the propertyinfo. It has been cached by the runtime internally [RuntimeTypeCache](https://source.dot.net/#System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs,d5408107ab8096c8). It would be cheap when you access it second time. – joe May 13 '21 at 04:32
38

@Oliver's answer as an extension method (which I think suits it well)

public static string PropertyList(this object obj)
{
  var props = obj.GetType().GetProperties();
  var sb = new StringBuilder();
  foreach (var p in props)
  {
    sb.AppendLine(p.Name + ": " + p.GetValue(obj, null));
  }
  return sb.ToString();
}
PandaWood
  • 8,086
  • 9
  • 49
  • 54
  • 1
    The same short form: public static string AsString(this object convertMe) => string.Join("\n",convertMe.GetType().GetProperties().Select(prop => $"{prop.Name}: {prop.GetValue(convertMe, null)}")); – tire0011 Jun 09 '21 at 13:30
4

You can do this via reflection.

PropertyInfo[] properties = MyClass.GetType().GetProperties();
foreach(PropertyInfo prop in properties)
{
...
}
Yakimych
  • 17,612
  • 7
  • 52
  • 69
Dennis
  • 2,132
  • 3
  • 21
  • 28
2

You can take inspiration from a more elaborate introspection of state from the StatePrinter package class introspector

Carlo V. Dango
  • 13,322
  • 16
  • 71
  • 114
1

If you have access to the code of the class you need then you can just override ToString() method. If not then you can use Reflections to read information from the Type object:

typeof(YourClass).GetProperties()
Andrew Bezzub
  • 15,744
  • 7
  • 51
  • 73