Fetch and print all properties of an object graph as stringReplace all occurrences with the properties of an...

What kind of wire should I use to pigtail an outlet?

Are Finite Automata Turing Complete?

How to append a matrix element by element

Why would people reject a god's purely beneficial blessing?

How to reply to small talk/random facts in a non-offensive way?

Why is there no havdallah when going from Yom Tov into Shabbat?

How can I convince my reader that I will not use a certain trope?

Fetch and print all properties of an object graph as string

Animation advice please

Should I include salary information on my CV?

Do equal angles necessarily mean a polygon is regular?

Is there any evidence that the small canisters (10 liters) of 95% oxygen actually help with altitude sickness?

Inverse-quotes-quine

Can ADFS connect to other SSO services?

What sort of mathematical problems are there in AI that people are working on?

Are there any vegetarian astronauts?

Short and long term plans in a closed game in the Sicilian Defense

Why do some professors with PhDs leave their professorships to teach high school?

Archery in modern conflicts

Is there any set of 2-6 notes that doesn't have a chord name?

C-152 carb heat on before landing in hot weather?

Is my Rep in Stack-Exchange Form?

Why is the voltage measurement of this circuit different when the switch is on?

Does ultrasonic bath cleaning damage laboratory volumetric glassware calibration?



Fetch and print all properties of an object graph as string


Replace all occurrences with the properties of an objectReturn IEnumerable<KeyValuePair> from a private method; use Dictionary or anon. type?Pattern-matching-esque codeImplementation of stackWPF object model control with singleton and static messenger? ConditionalWeakTable?Safely order a list of objects by DateTimeBasic Pong GameTest all properties with single test functionPolymorphic DBConnector class exerciseEnumerate all members and types with specific attributes






.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ margin-bottom:0;
}







3












$begingroup$


Below I have setup an extension method that takes any object, cycles through its properties, and prints each out to a Console window in the format Name: Value.




Specification / Scope:




  • Handle only single objects (no collections)




code



public static string PropertiesToString<T>(this T obj, int tabs = 0) where T : class
{
int initTabs = tabs;
string result = string.Empty;
PropertyInfo[] propertyInfo = obj.GetType().GetProperties();
foreach (PropertyInfo property in propertyInfo)
{
string name = property.Name;
object value = property.GetValue(obj, null);
Type valueType = value.GetType();
if (valueType.IsValueType || valueType.Name.Equals("String"))
{
for (int i = 0; i < tabs; i++)
{
result += " ";
}
result += string.Format("{0}: {1}n", name, value == null ? string.Empty : value.ToString());
}
else
{
result += string.Format("{0}:n", name);
result += value.PropertiesToString(++tabs);
}
tabs = initTabs;
}
return result;
}


Here is a class I'm using to test this code, along with the lines responsible for creating an instance of the class and writing its properties to the Console:



Class:



public class Circle : IShape
{
public Circle(double x, double y, double radius)
{
Center = new Point
{
X = x,
Y = y
};
Radius = radius;
}

public Point Center { get; set; }
public double Radius { get; set; }

public double Area(int precision = 2)
{
return Math.Round(Radius * Radius * Math.PI, precision);
}
}


Main:



public static void Main(string[] args)
{
IShape circle = new Circle(5, 5, 10);
Console.WriteLine(circle.PropertiesToString());
Console.ReadLine();
}


The above method will also cycle through nested objects and print those to the Console as well, adding in tabs for readability's sake.



I'm kind of unfamiliar with System.Reflection and was wondering if there was a more efficient way I could approach doing something like this.










share|improve this question











$endgroup$












  • $begingroup$
    The null-references are handled within the method by just adding string.Empty to result if value == null. Otherwise I was only considering using this method for single objects, not collections.
    $endgroup$
    – Delfino
    7 hours ago


















3












$begingroup$


Below I have setup an extension method that takes any object, cycles through its properties, and prints each out to a Console window in the format Name: Value.




Specification / Scope:




  • Handle only single objects (no collections)




code



public static string PropertiesToString<T>(this T obj, int tabs = 0) where T : class
{
int initTabs = tabs;
string result = string.Empty;
PropertyInfo[] propertyInfo = obj.GetType().GetProperties();
foreach (PropertyInfo property in propertyInfo)
{
string name = property.Name;
object value = property.GetValue(obj, null);
Type valueType = value.GetType();
if (valueType.IsValueType || valueType.Name.Equals("String"))
{
for (int i = 0; i < tabs; i++)
{
result += " ";
}
result += string.Format("{0}: {1}n", name, value == null ? string.Empty : value.ToString());
}
else
{
result += string.Format("{0}:n", name);
result += value.PropertiesToString(++tabs);
}
tabs = initTabs;
}
return result;
}


Here is a class I'm using to test this code, along with the lines responsible for creating an instance of the class and writing its properties to the Console:



Class:



public class Circle : IShape
{
public Circle(double x, double y, double radius)
{
Center = new Point
{
X = x,
Y = y
};
Radius = radius;
}

public Point Center { get; set; }
public double Radius { get; set; }

public double Area(int precision = 2)
{
return Math.Round(Radius * Radius * Math.PI, precision);
}
}


Main:



public static void Main(string[] args)
{
IShape circle = new Circle(5, 5, 10);
Console.WriteLine(circle.PropertiesToString());
Console.ReadLine();
}


The above method will also cycle through nested objects and print those to the Console as well, adding in tabs for readability's sake.



I'm kind of unfamiliar with System.Reflection and was wondering if there was a more efficient way I could approach doing something like this.










share|improve this question











$endgroup$












  • $begingroup$
    The null-references are handled within the method by just adding string.Empty to result if value == null. Otherwise I was only considering using this method for single objects, not collections.
    $endgroup$
    – Delfino
    7 hours ago














3












3








3





$begingroup$


Below I have setup an extension method that takes any object, cycles through its properties, and prints each out to a Console window in the format Name: Value.




Specification / Scope:




  • Handle only single objects (no collections)




code



public static string PropertiesToString<T>(this T obj, int tabs = 0) where T : class
{
int initTabs = tabs;
string result = string.Empty;
PropertyInfo[] propertyInfo = obj.GetType().GetProperties();
foreach (PropertyInfo property in propertyInfo)
{
string name = property.Name;
object value = property.GetValue(obj, null);
Type valueType = value.GetType();
if (valueType.IsValueType || valueType.Name.Equals("String"))
{
for (int i = 0; i < tabs; i++)
{
result += " ";
}
result += string.Format("{0}: {1}n", name, value == null ? string.Empty : value.ToString());
}
else
{
result += string.Format("{0}:n", name);
result += value.PropertiesToString(++tabs);
}
tabs = initTabs;
}
return result;
}


Here is a class I'm using to test this code, along with the lines responsible for creating an instance of the class and writing its properties to the Console:



Class:



public class Circle : IShape
{
public Circle(double x, double y, double radius)
{
Center = new Point
{
X = x,
Y = y
};
Radius = radius;
}

public Point Center { get; set; }
public double Radius { get; set; }

public double Area(int precision = 2)
{
return Math.Round(Radius * Radius * Math.PI, precision);
}
}


Main:



public static void Main(string[] args)
{
IShape circle = new Circle(5, 5, 10);
Console.WriteLine(circle.PropertiesToString());
Console.ReadLine();
}


The above method will also cycle through nested objects and print those to the Console as well, adding in tabs for readability's sake.



I'm kind of unfamiliar with System.Reflection and was wondering if there was a more efficient way I could approach doing something like this.










share|improve this question











$endgroup$




Below I have setup an extension method that takes any object, cycles through its properties, and prints each out to a Console window in the format Name: Value.




Specification / Scope:




  • Handle only single objects (no collections)




code



public static string PropertiesToString<T>(this T obj, int tabs = 0) where T : class
{
int initTabs = tabs;
string result = string.Empty;
PropertyInfo[] propertyInfo = obj.GetType().GetProperties();
foreach (PropertyInfo property in propertyInfo)
{
string name = property.Name;
object value = property.GetValue(obj, null);
Type valueType = value.GetType();
if (valueType.IsValueType || valueType.Name.Equals("String"))
{
for (int i = 0; i < tabs; i++)
{
result += " ";
}
result += string.Format("{0}: {1}n", name, value == null ? string.Empty : value.ToString());
}
else
{
result += string.Format("{0}:n", name);
result += value.PropertiesToString(++tabs);
}
tabs = initTabs;
}
return result;
}


Here is a class I'm using to test this code, along with the lines responsible for creating an instance of the class and writing its properties to the Console:



Class:



public class Circle : IShape
{
public Circle(double x, double y, double radius)
{
Center = new Point
{
X = x,
Y = y
};
Radius = radius;
}

public Point Center { get; set; }
public double Radius { get; set; }

public double Area(int precision = 2)
{
return Math.Round(Radius * Radius * Math.PI, precision);
}
}


Main:



public static void Main(string[] args)
{
IShape circle = new Circle(5, 5, 10);
Console.WriteLine(circle.PropertiesToString());
Console.ReadLine();
}


The above method will also cycle through nested objects and print those to the Console as well, adding in tabs for readability's sake.



I'm kind of unfamiliar with System.Reflection and was wondering if there was a more efficient way I could approach doing something like this.







c# beginner recursion reflection extension-methods






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited 7 hours ago









dfhwze

3,1161 gold badge6 silver badges31 bronze badges




3,1161 gold badge6 silver badges31 bronze badges










asked 8 hours ago









DelfinoDelfino

1776 bronze badges




1776 bronze badges












  • $begingroup$
    The null-references are handled within the method by just adding string.Empty to result if value == null. Otherwise I was only considering using this method for single objects, not collections.
    $endgroup$
    – Delfino
    7 hours ago


















  • $begingroup$
    The null-references are handled within the method by just adding string.Empty to result if value == null. Otherwise I was only considering using this method for single objects, not collections.
    $endgroup$
    – Delfino
    7 hours ago
















$begingroup$
The null-references are handled within the method by just adding string.Empty to result if value == null. Otherwise I was only considering using this method for single objects, not collections.
$endgroup$
– Delfino
7 hours ago




$begingroup$
The null-references are handled within the method by just adding string.Empty to result if value == null. Otherwise I was only considering using this method for single objects, not collections.
$endgroup$
– Delfino
7 hours ago










2 Answers
2






active

oldest

votes


















3












$begingroup$

You should have a guard clause against someone passing in null



PropertyInfo has a PropertyType and you should use that instead of



 Type valueType = value.GetType();


as if value is null you will get a null reference error where PropertyType will give you the type of the property.



You will need to fix this line as well if value is null. Again you will get a null reference error



        result += value.PropertiesToString(++tabs);


Better to compare types then their names. Instead of



valueType.Name.Equals("String")


use



valueType == typeof(string)


You should separate out gathering the properties and displaying them. I would have an extension method that returned IEnumerable> and then you could use linq to convert that into the strings and how you want it displayed.



Other things to consider is if you have two object that reference each other you will get a stack overflow.



I would recommend looking at https://docs.microsoft.com/en-us/dotnet/api/system.runtime.serialization.objectmanager?view=netframework-4.8 to see an example of an object walker. I think making the class IEnumerable is a bit confusion when learning and they would have been better making a method that returns IEnumerable. still a good place to start when learning about walking an object graph.






share|improve this answer









$endgroup$





















    3












    $begingroup$

    Bug



    Your code does not handle value == null everywhere.




    object value = property.GetValue(obj, null);
    Type valueType = value.GetType(); // <- can throw the infamous NRE



    Review




    • Use a clear method name RenderProperties.


    • int tabs = 0 does not allow flexibility for rendering indents. Use string indent = "t" instead.


    • this T obj is fine, but I would prefer this T source.


    • string result = string.Empty; and result += .. use a System.Text.StringBuilder instead; much better memory management.

    • Use var syntax var resultBuilder = new StringBuilder();.


    • obj.GetType().GetProperties(); should be extended to public, flatten hierarchy, instance BindingFlags properties with CanRead, GetGetMethod(false) != null and GetIndexParameters().Length == 0 to only include the publically accessible getter properties of the instance.


    • valueType.Name.Equals("String") should be value is String. But perhaps you need a better strategy for determining which objects are complex ..


    • for (int i = 0; i < tabs; i++) { result += .. gets replaced completely with indent as earlier specified.


    • string.Format("{0}: {1}n" .. should use Environment.NewLine, or even better use an overload on StringBuilder called AppendFormatLine. Same thing in the else clause.


    • PropertiesToString(value, ++tabs); can be replaced by PropertiesToString(value, indent + indent);.


    Your Code Edited




    • I have decoupled retrieving properties from rendering. However, in another answer was suggested to go further and use a tree walker to adhere to best practices. That would be even better.


    • I am asserting array or other collections do not require their items to be visited, and the complete object graph is a tree. You did never specify how to handle cyclic graphs, so they are out of scope :)



    Render properties:



    public static string RenderProperties<T>(this T source, string indent = "t") 
    where T : class
    {
    if (source == null) return string.Empty;
    indent = indent ?? string.Empty;
    var builder = new StringBuilder();
    var properties = GetAccessibleProperties(source);

    foreach (var property in properties)
    {
    RenderProperty(property, source, builder, indent);
    }

    return builder.ToString();
    }


    Render property:



    private static void RenderProperty(
    PropertyInfo property, object parent, StringBuilder builder, string indent)
    {
    Debug.Assert(property != null);
    Debug.Assert(parent != null);
    Debug.Assert(builder != null);
    Debug.Assert(indent != null);

    var name = property.Name;
    var value = property.GetValue(parent, null); // <- need to handle exception?

    if (value == null)
    {
    builder.AppendLine($"{indent}{name}: ");
    }
    else if (value.GetType().IsValueType || value is string)
    {
    builder.AppendLine($"{indent}{name}: {value}");
    }
    else
    {
    builder.AppendLine(RenderProperties(value, indent + indent));
    }
    }


    Get accessible properties:



    private static IEnumerable<PropertyInfo> GetAccessibleProperties(object source)
    {
    Debug.Assert(source != null);
    // optimized for readibility over performance ->
    var properties = source.GetType()
    .GetProperties(
    BindingFlags.Instance // only instance properties
    | BindingFlags.FlattenHierarchy // include base class properties
    | BindingFlags.Public) // publicly accessible only
    .Where(x =>
    x.CanRead // must have getter
    && x.GetGetMethod(false) != null // must have public getter
    && x.GetIndexParameters().Length == 0); // must not be an indexer
    return properties;
    }





    share|improve this answer











    $endgroup$
















      Your Answer






      StackExchange.ifUsing("editor", function () {
      StackExchange.using("externalEditor", function () {
      StackExchange.using("snippets", function () {
      StackExchange.snippets.init();
      });
      });
      }, "code-snippets");

      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "196"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: false,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: null,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });














      draft saved

      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f222729%2ffetch-and-print-all-properties-of-an-object-graph-as-string%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      2 Answers
      2






      active

      oldest

      votes








      2 Answers
      2






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      3












      $begingroup$

      You should have a guard clause against someone passing in null



      PropertyInfo has a PropertyType and you should use that instead of



       Type valueType = value.GetType();


      as if value is null you will get a null reference error where PropertyType will give you the type of the property.



      You will need to fix this line as well if value is null. Again you will get a null reference error



              result += value.PropertiesToString(++tabs);


      Better to compare types then their names. Instead of



      valueType.Name.Equals("String")


      use



      valueType == typeof(string)


      You should separate out gathering the properties and displaying them. I would have an extension method that returned IEnumerable> and then you could use linq to convert that into the strings and how you want it displayed.



      Other things to consider is if you have two object that reference each other you will get a stack overflow.



      I would recommend looking at https://docs.microsoft.com/en-us/dotnet/api/system.runtime.serialization.objectmanager?view=netframework-4.8 to see an example of an object walker. I think making the class IEnumerable is a bit confusion when learning and they would have been better making a method that returns IEnumerable. still a good place to start when learning about walking an object graph.






      share|improve this answer









      $endgroup$


















        3












        $begingroup$

        You should have a guard clause against someone passing in null



        PropertyInfo has a PropertyType and you should use that instead of



         Type valueType = value.GetType();


        as if value is null you will get a null reference error where PropertyType will give you the type of the property.



        You will need to fix this line as well if value is null. Again you will get a null reference error



                result += value.PropertiesToString(++tabs);


        Better to compare types then their names. Instead of



        valueType.Name.Equals("String")


        use



        valueType == typeof(string)


        You should separate out gathering the properties and displaying them. I would have an extension method that returned IEnumerable> and then you could use linq to convert that into the strings and how you want it displayed.



        Other things to consider is if you have two object that reference each other you will get a stack overflow.



        I would recommend looking at https://docs.microsoft.com/en-us/dotnet/api/system.runtime.serialization.objectmanager?view=netframework-4.8 to see an example of an object walker. I think making the class IEnumerable is a bit confusion when learning and they would have been better making a method that returns IEnumerable. still a good place to start when learning about walking an object graph.






        share|improve this answer









        $endgroup$
















          3












          3








          3





          $begingroup$

          You should have a guard clause against someone passing in null



          PropertyInfo has a PropertyType and you should use that instead of



           Type valueType = value.GetType();


          as if value is null you will get a null reference error where PropertyType will give you the type of the property.



          You will need to fix this line as well if value is null. Again you will get a null reference error



                  result += value.PropertiesToString(++tabs);


          Better to compare types then their names. Instead of



          valueType.Name.Equals("String")


          use



          valueType == typeof(string)


          You should separate out gathering the properties and displaying them. I would have an extension method that returned IEnumerable> and then you could use linq to convert that into the strings and how you want it displayed.



          Other things to consider is if you have two object that reference each other you will get a stack overflow.



          I would recommend looking at https://docs.microsoft.com/en-us/dotnet/api/system.runtime.serialization.objectmanager?view=netframework-4.8 to see an example of an object walker. I think making the class IEnumerable is a bit confusion when learning and they would have been better making a method that returns IEnumerable. still a good place to start when learning about walking an object graph.






          share|improve this answer









          $endgroup$



          You should have a guard clause against someone passing in null



          PropertyInfo has a PropertyType and you should use that instead of



           Type valueType = value.GetType();


          as if value is null you will get a null reference error where PropertyType will give you the type of the property.



          You will need to fix this line as well if value is null. Again you will get a null reference error



                  result += value.PropertiesToString(++tabs);


          Better to compare types then their names. Instead of



          valueType.Name.Equals("String")


          use



          valueType == typeof(string)


          You should separate out gathering the properties and displaying them. I would have an extension method that returned IEnumerable> and then you could use linq to convert that into the strings and how you want it displayed.



          Other things to consider is if you have two object that reference each other you will get a stack overflow.



          I would recommend looking at https://docs.microsoft.com/en-us/dotnet/api/system.runtime.serialization.objectmanager?view=netframework-4.8 to see an example of an object walker. I think making the class IEnumerable is a bit confusion when learning and they would have been better making a method that returns IEnumerable. still a good place to start when learning about walking an object graph.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered 7 hours ago









          CharlesNRiceCharlesNRice

          2,2896 silver badges13 bronze badges




          2,2896 silver badges13 bronze badges

























              3












              $begingroup$

              Bug



              Your code does not handle value == null everywhere.




              object value = property.GetValue(obj, null);
              Type valueType = value.GetType(); // <- can throw the infamous NRE



              Review




              • Use a clear method name RenderProperties.


              • int tabs = 0 does not allow flexibility for rendering indents. Use string indent = "t" instead.


              • this T obj is fine, but I would prefer this T source.


              • string result = string.Empty; and result += .. use a System.Text.StringBuilder instead; much better memory management.

              • Use var syntax var resultBuilder = new StringBuilder();.


              • obj.GetType().GetProperties(); should be extended to public, flatten hierarchy, instance BindingFlags properties with CanRead, GetGetMethod(false) != null and GetIndexParameters().Length == 0 to only include the publically accessible getter properties of the instance.


              • valueType.Name.Equals("String") should be value is String. But perhaps you need a better strategy for determining which objects are complex ..


              • for (int i = 0; i < tabs; i++) { result += .. gets replaced completely with indent as earlier specified.


              • string.Format("{0}: {1}n" .. should use Environment.NewLine, or even better use an overload on StringBuilder called AppendFormatLine. Same thing in the else clause.


              • PropertiesToString(value, ++tabs); can be replaced by PropertiesToString(value, indent + indent);.


              Your Code Edited




              • I have decoupled retrieving properties from rendering. However, in another answer was suggested to go further and use a tree walker to adhere to best practices. That would be even better.


              • I am asserting array or other collections do not require their items to be visited, and the complete object graph is a tree. You did never specify how to handle cyclic graphs, so they are out of scope :)



              Render properties:



              public static string RenderProperties<T>(this T source, string indent = "t") 
              where T : class
              {
              if (source == null) return string.Empty;
              indent = indent ?? string.Empty;
              var builder = new StringBuilder();
              var properties = GetAccessibleProperties(source);

              foreach (var property in properties)
              {
              RenderProperty(property, source, builder, indent);
              }

              return builder.ToString();
              }


              Render property:



              private static void RenderProperty(
              PropertyInfo property, object parent, StringBuilder builder, string indent)
              {
              Debug.Assert(property != null);
              Debug.Assert(parent != null);
              Debug.Assert(builder != null);
              Debug.Assert(indent != null);

              var name = property.Name;
              var value = property.GetValue(parent, null); // <- need to handle exception?

              if (value == null)
              {
              builder.AppendLine($"{indent}{name}: ");
              }
              else if (value.GetType().IsValueType || value is string)
              {
              builder.AppendLine($"{indent}{name}: {value}");
              }
              else
              {
              builder.AppendLine(RenderProperties(value, indent + indent));
              }
              }


              Get accessible properties:



              private static IEnumerable<PropertyInfo> GetAccessibleProperties(object source)
              {
              Debug.Assert(source != null);
              // optimized for readibility over performance ->
              var properties = source.GetType()
              .GetProperties(
              BindingFlags.Instance // only instance properties
              | BindingFlags.FlattenHierarchy // include base class properties
              | BindingFlags.Public) // publicly accessible only
              .Where(x =>
              x.CanRead // must have getter
              && x.GetGetMethod(false) != null // must have public getter
              && x.GetIndexParameters().Length == 0); // must not be an indexer
              return properties;
              }





              share|improve this answer











              $endgroup$


















                3












                $begingroup$

                Bug



                Your code does not handle value == null everywhere.




                object value = property.GetValue(obj, null);
                Type valueType = value.GetType(); // <- can throw the infamous NRE



                Review




                • Use a clear method name RenderProperties.


                • int tabs = 0 does not allow flexibility for rendering indents. Use string indent = "t" instead.


                • this T obj is fine, but I would prefer this T source.


                • string result = string.Empty; and result += .. use a System.Text.StringBuilder instead; much better memory management.

                • Use var syntax var resultBuilder = new StringBuilder();.


                • obj.GetType().GetProperties(); should be extended to public, flatten hierarchy, instance BindingFlags properties with CanRead, GetGetMethod(false) != null and GetIndexParameters().Length == 0 to only include the publically accessible getter properties of the instance.


                • valueType.Name.Equals("String") should be value is String. But perhaps you need a better strategy for determining which objects are complex ..


                • for (int i = 0; i < tabs; i++) { result += .. gets replaced completely with indent as earlier specified.


                • string.Format("{0}: {1}n" .. should use Environment.NewLine, or even better use an overload on StringBuilder called AppendFormatLine. Same thing in the else clause.


                • PropertiesToString(value, ++tabs); can be replaced by PropertiesToString(value, indent + indent);.


                Your Code Edited




                • I have decoupled retrieving properties from rendering. However, in another answer was suggested to go further and use a tree walker to adhere to best practices. That would be even better.


                • I am asserting array or other collections do not require their items to be visited, and the complete object graph is a tree. You did never specify how to handle cyclic graphs, so they are out of scope :)



                Render properties:



                public static string RenderProperties<T>(this T source, string indent = "t") 
                where T : class
                {
                if (source == null) return string.Empty;
                indent = indent ?? string.Empty;
                var builder = new StringBuilder();
                var properties = GetAccessibleProperties(source);

                foreach (var property in properties)
                {
                RenderProperty(property, source, builder, indent);
                }

                return builder.ToString();
                }


                Render property:



                private static void RenderProperty(
                PropertyInfo property, object parent, StringBuilder builder, string indent)
                {
                Debug.Assert(property != null);
                Debug.Assert(parent != null);
                Debug.Assert(builder != null);
                Debug.Assert(indent != null);

                var name = property.Name;
                var value = property.GetValue(parent, null); // <- need to handle exception?

                if (value == null)
                {
                builder.AppendLine($"{indent}{name}: ");
                }
                else if (value.GetType().IsValueType || value is string)
                {
                builder.AppendLine($"{indent}{name}: {value}");
                }
                else
                {
                builder.AppendLine(RenderProperties(value, indent + indent));
                }
                }


                Get accessible properties:



                private static IEnumerable<PropertyInfo> GetAccessibleProperties(object source)
                {
                Debug.Assert(source != null);
                // optimized for readibility over performance ->
                var properties = source.GetType()
                .GetProperties(
                BindingFlags.Instance // only instance properties
                | BindingFlags.FlattenHierarchy // include base class properties
                | BindingFlags.Public) // publicly accessible only
                .Where(x =>
                x.CanRead // must have getter
                && x.GetGetMethod(false) != null // must have public getter
                && x.GetIndexParameters().Length == 0); // must not be an indexer
                return properties;
                }





                share|improve this answer











                $endgroup$
















                  3












                  3








                  3





                  $begingroup$

                  Bug



                  Your code does not handle value == null everywhere.




                  object value = property.GetValue(obj, null);
                  Type valueType = value.GetType(); // <- can throw the infamous NRE



                  Review




                  • Use a clear method name RenderProperties.


                  • int tabs = 0 does not allow flexibility for rendering indents. Use string indent = "t" instead.


                  • this T obj is fine, but I would prefer this T source.


                  • string result = string.Empty; and result += .. use a System.Text.StringBuilder instead; much better memory management.

                  • Use var syntax var resultBuilder = new StringBuilder();.


                  • obj.GetType().GetProperties(); should be extended to public, flatten hierarchy, instance BindingFlags properties with CanRead, GetGetMethod(false) != null and GetIndexParameters().Length == 0 to only include the publically accessible getter properties of the instance.


                  • valueType.Name.Equals("String") should be value is String. But perhaps you need a better strategy for determining which objects are complex ..


                  • for (int i = 0; i < tabs; i++) { result += .. gets replaced completely with indent as earlier specified.


                  • string.Format("{0}: {1}n" .. should use Environment.NewLine, or even better use an overload on StringBuilder called AppendFormatLine. Same thing in the else clause.


                  • PropertiesToString(value, ++tabs); can be replaced by PropertiesToString(value, indent + indent);.


                  Your Code Edited




                  • I have decoupled retrieving properties from rendering. However, in another answer was suggested to go further and use a tree walker to adhere to best practices. That would be even better.


                  • I am asserting array or other collections do not require their items to be visited, and the complete object graph is a tree. You did never specify how to handle cyclic graphs, so they are out of scope :)



                  Render properties:



                  public static string RenderProperties<T>(this T source, string indent = "t") 
                  where T : class
                  {
                  if (source == null) return string.Empty;
                  indent = indent ?? string.Empty;
                  var builder = new StringBuilder();
                  var properties = GetAccessibleProperties(source);

                  foreach (var property in properties)
                  {
                  RenderProperty(property, source, builder, indent);
                  }

                  return builder.ToString();
                  }


                  Render property:



                  private static void RenderProperty(
                  PropertyInfo property, object parent, StringBuilder builder, string indent)
                  {
                  Debug.Assert(property != null);
                  Debug.Assert(parent != null);
                  Debug.Assert(builder != null);
                  Debug.Assert(indent != null);

                  var name = property.Name;
                  var value = property.GetValue(parent, null); // <- need to handle exception?

                  if (value == null)
                  {
                  builder.AppendLine($"{indent}{name}: ");
                  }
                  else if (value.GetType().IsValueType || value is string)
                  {
                  builder.AppendLine($"{indent}{name}: {value}");
                  }
                  else
                  {
                  builder.AppendLine(RenderProperties(value, indent + indent));
                  }
                  }


                  Get accessible properties:



                  private static IEnumerable<PropertyInfo> GetAccessibleProperties(object source)
                  {
                  Debug.Assert(source != null);
                  // optimized for readibility over performance ->
                  var properties = source.GetType()
                  .GetProperties(
                  BindingFlags.Instance // only instance properties
                  | BindingFlags.FlattenHierarchy // include base class properties
                  | BindingFlags.Public) // publicly accessible only
                  .Where(x =>
                  x.CanRead // must have getter
                  && x.GetGetMethod(false) != null // must have public getter
                  && x.GetIndexParameters().Length == 0); // must not be an indexer
                  return properties;
                  }





                  share|improve this answer











                  $endgroup$



                  Bug



                  Your code does not handle value == null everywhere.




                  object value = property.GetValue(obj, null);
                  Type valueType = value.GetType(); // <- can throw the infamous NRE



                  Review




                  • Use a clear method name RenderProperties.


                  • int tabs = 0 does not allow flexibility for rendering indents. Use string indent = "t" instead.


                  • this T obj is fine, but I would prefer this T source.


                  • string result = string.Empty; and result += .. use a System.Text.StringBuilder instead; much better memory management.

                  • Use var syntax var resultBuilder = new StringBuilder();.


                  • obj.GetType().GetProperties(); should be extended to public, flatten hierarchy, instance BindingFlags properties with CanRead, GetGetMethod(false) != null and GetIndexParameters().Length == 0 to only include the publically accessible getter properties of the instance.


                  • valueType.Name.Equals("String") should be value is String. But perhaps you need a better strategy for determining which objects are complex ..


                  • for (int i = 0; i < tabs; i++) { result += .. gets replaced completely with indent as earlier specified.


                  • string.Format("{0}: {1}n" .. should use Environment.NewLine, or even better use an overload on StringBuilder called AppendFormatLine. Same thing in the else clause.


                  • PropertiesToString(value, ++tabs); can be replaced by PropertiesToString(value, indent + indent);.


                  Your Code Edited




                  • I have decoupled retrieving properties from rendering. However, in another answer was suggested to go further and use a tree walker to adhere to best practices. That would be even better.


                  • I am asserting array or other collections do not require their items to be visited, and the complete object graph is a tree. You did never specify how to handle cyclic graphs, so they are out of scope :)



                  Render properties:



                  public static string RenderProperties<T>(this T source, string indent = "t") 
                  where T : class
                  {
                  if (source == null) return string.Empty;
                  indent = indent ?? string.Empty;
                  var builder = new StringBuilder();
                  var properties = GetAccessibleProperties(source);

                  foreach (var property in properties)
                  {
                  RenderProperty(property, source, builder, indent);
                  }

                  return builder.ToString();
                  }


                  Render property:



                  private static void RenderProperty(
                  PropertyInfo property, object parent, StringBuilder builder, string indent)
                  {
                  Debug.Assert(property != null);
                  Debug.Assert(parent != null);
                  Debug.Assert(builder != null);
                  Debug.Assert(indent != null);

                  var name = property.Name;
                  var value = property.GetValue(parent, null); // <- need to handle exception?

                  if (value == null)
                  {
                  builder.AppendLine($"{indent}{name}: ");
                  }
                  else if (value.GetType().IsValueType || value is string)
                  {
                  builder.AppendLine($"{indent}{name}: {value}");
                  }
                  else
                  {
                  builder.AppendLine(RenderProperties(value, indent + indent));
                  }
                  }


                  Get accessible properties:



                  private static IEnumerable<PropertyInfo> GetAccessibleProperties(object source)
                  {
                  Debug.Assert(source != null);
                  // optimized for readibility over performance ->
                  var properties = source.GetType()
                  .GetProperties(
                  BindingFlags.Instance // only instance properties
                  | BindingFlags.FlattenHierarchy // include base class properties
                  | BindingFlags.Public) // publicly accessible only
                  .Where(x =>
                  x.CanRead // must have getter
                  && x.GetGetMethod(false) != null // must have public getter
                  && x.GetIndexParameters().Length == 0); // must not be an indexer
                  return properties;
                  }






                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited 5 hours ago









                  t3chb0t

                  36k7 gold badges58 silver badges133 bronze badges




                  36k7 gold badges58 silver badges133 bronze badges










                  answered 6 hours ago









                  dfhwzedfhwze

                  3,1161 gold badge6 silver badges31 bronze badges




                  3,1161 gold badge6 silver badges31 bronze badges






























                      draft saved

                      draft discarded




















































                      Thanks for contributing an answer to Code Review Stack Exchange!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      Use MathJax to format equations. MathJax reference.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f222729%2ffetch-and-print-all-properties-of-an-object-graph-as-string%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      Taj Mahal Inhaltsverzeichnis Aufbau | Geschichte | 350-Jahr-Feier | Heutige Bedeutung | Siehe auch |...

                      Baia Sprie Cuprins Etimologie | Istorie | Demografie | Politică și administrație | Arii naturale...

                      Nicolae Petrescu-Găină Cuprins Biografie | Opera | In memoriam | Varia | Controverse, incertitudini...