Tuesday, July 28, 2009

PHP var_dump() method for C#

In PHP there is the var_dump() and print_r() function that displays the contents of an object.
Sometimes i miss this functionality in ASP.NET, especially when you are working with large objects and don't want to step into your code every time.
I came up with this method to list all the properties of a given object by name and value.
It also lists any child properties recursively, until it reaches a certain level of recursion (in this case it stops after 5 recursions).

using System;
using System.Text;
using System.Reflection;
using System.Collections;

public string var_dump(object obj, int recursion)
{
StringBuilder result = new StringBuilder();

// Protect the method against endless recursion
if (recursion < 5)
{
// Determine object type
Type t = obj.GetType();

// Get array with properties for this object
PropertyInfo[] properties = t.GetProperties();

foreach (PropertyInfo property in properties)
{
try
{
// Get the property value
object value = property.GetValue(obj, null);

// Create indenting string to put in front of properties of a deeper level
// We'll need this when we display the property name and value
string indent = String.Empty;
string spaces = "| ";
string trail = "|...";

if (recursion > 0)
{
indent = new StringBuilder(trail).Insert(0, spaces, recursion - 1).ToString();
}

if (value != null)
{
// If the value is a string, add quotation marks
string displayValue = value.ToString();
if (value is string) displayValue = String.Concat('"', displayValue, '"');

// Add property name and value to return string
result.AppendFormat("{0}{1} = {2}\n", indent, property.Name, displayValue);

try
{
if (!(value is ICollection))
{
// Call var_dump() again to list child properties
// This throws an exception if the current property value
// is of an unsupported type (eg. it has not properties)
result.Append(var_dump(value, recursion + 1));
}
else
{
// 2009-07-29: added support for collections
// The value is a collection (eg. it's an arraylist or generic list)
// so loop through its elements and dump their properties
int elementCount = 0;
foreach (object element in ((ICollection)value))
{
string elementName = String.Format("{0}[{1}]", property.Name, elementCount);
indent = new StringBuilder(trail).Insert(0, spaces, recursion).ToString();

// Display the collection element name and type
result.AppendFormat("{0}{1} = {2}\n", indent, elementName, element.ToString());

// Display the child properties
result.Append(var_dump(element, recursion + 2));
elementCount++;
}

result.Append(var_dump(value, recursion + 1));
}
}
catch { }
}
else
{
// Add empty (null) property to return string
result.AppendFormat("{0}{1} = {2}\n", indent, property.Name, "null");
}
}
catch
{
// Some properties will throw an exception on property.GetValue()
// I don't know exactly why this happens, so for now i will ignore them...
}
}
}

return result.ToString();
}

For my example i used an object called Category, but this can be an instance of any class.
Category has a few basic properties and one called Parent which holds another Category object (so we have child properties).
I call the method like this:

Category category = LoadCategory(123);
Response.Write(var_dump(myObject, 0)); // Always start at recursion 0,
// you might want to add an override
// that sets this argument by default

The output of this example is:

CategoryId = 685
ParentId = 287
Name = "Beans"
Description = ""
Parent = MyProject.Data.Category
|...CategoryId = 287
|...ParentId = 174
|...Name = "Cheap food"
|...Description = ""
|...Parent = MyProject.Data.Category
| |...CategoryId = 174
| |...ParentId = 173
| |...Name = "Food"
| |...Description = ""
| |...Parent = MyProject.Data.Category
| | |...CategoryId = 173
| | |...ParentId = 0
| | |...Name = "Things to eat"
| | |...Description = ""
| | |...Parent = null
| | |...Count = 0
| |...Count = 0
|...Count = 13
Count = 0
TestList = System.Collections.Generic.List`1[MyProject.Data.Category]
|...TestList[0] = MyProject.Data.Category
| |...CategoryId = 1
| |...ParentId = 0
| |...Name = "Bla"
| |...Description = ""
| |...Count = 0
|...Count = 1

It's not exactly PHP's var_dump() method, but it works for me! :)
I might expand it some day so it dumps array/list elements too and maybe add some XHTML formatting.

Update on 29-07-2009:
Added support for collection properties.

/Ruud

16 comments:

  1. Quite nice, will try this shortly.. thanks for publishing this!

    ReplyDelete
  2. Thanks for the code ;) I didn't see the list of "using" commands, but here are the ones I used for it:

    using System;
    using System.Text;
    using System.Reflection;
    using System.Collections;

    I hope that helps. It took me a while to figure out which ones were needed lol.
    Thanks again for the code.

    ReplyDelete
  3. Thanks Floydian.
    I added the using statements to the code.

    You can let Visual Studio find them for you btw.
    The missing classes will be underlined in the IDE (after the failed build) and if you put your cursor on them, a little red box will appear at the end of the class name.
    When you click on that box it searches for the required namespace and gives you the option to add it with a using statement :)

    ReplyDelete
  4. I´m sorry, i´m begginer in asp.net but a create a new web site. then a click right mouse button to solution->add new item->Class (c#), then i copy and paste your code and visual studio is repporting an error (You dont have a class in your code...)So i create a class , and then put your code in there. Class is public. Method var_dump is public. But if i wan´t use it in for example default.aspx.cs i thnik it will be like that Var_dump.var_dump ("this is text") but it not. Var_dump (is name of public class) var_dump(is name of public method) so why when i type Var_dump and then . (dot) the visual studio offer me only Equals or ReferenceEqual and don´t offer me a var_dump method?

    ReplyDelete
  5. i find out.No more help needed :-)

    ReplyDelete
  6. @Anonymous -> How did you resolve it?

    ReplyDelete
  7. ありがとうございます!
    助かりました!

    ReplyDelete
  8. Great solution! Thanks.
    I would like to add that using Environment.NewLine is more preferable than \n.
    Or at least we must use \r\n, because \n usage does not break lines in TextBoxes for example.

    ReplyDelete
  9. There seems to be a bug with the DateTime.Date property; repeating data as the .Date property returns a new DateTime object. Any possibility to cut it at one .Date property?

    ReplyDelete
  10. Hey thank you guy! I added a method in order to show the DisplayNameAttribute value if not null!

    Have a look (Please indent my code)



    public static string GetDisplayName(PropertyInfo info)
    {
    DisplayNameAttribute attr;
    attr = (DisplayNameAttribute)info.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();

    return (attr != null) ? attr.DisplayName :info.Name;
    }


    then I replaced property.Name with GetDisplayName(property)

    ReplyDelete
  11. It's not working for me for some reason?

    I passed a List(Of Integer) to it. i.e.

    Dim iList as New List(Of Integer)

    iList.Add(10);
    iList.Add(0);
    iList.Add(2345);

    var_dump(iList, 0)

    Any help please?

    ReplyDelete
  12. What is the licence on this code?

    ReplyDelete
  13. It's been 12 years old code! Great job!

    ReplyDelete