Make Debugging Easier with my Complex Object Visualizer

Posted on Updated on

Use Reflection to Display Complex Objects While Debugging!

Problem: it is hard to inspect complicated objects in the debugger. Lately, I’ve been testing a lot of web services and using complex objects form 3rd party libraries. These objects are big, complicated chunks of data. If you’re like me, you might feel frustrated inspecting them in the debugger. Nested properties can be a pain, especially in huge lists. What I find particularly irritating is, having scrolled deep, deep down into a big nested object, is accidentally clicking away and loosing the whole freakin’ thing! It would be better if I could dump the entire object somewhere like the output window or an unused TextBox. And that’s just what I’ve done!

Inspecting Complex Objects In the Debugger
When dealing with big classes, it can be challenging to find the value you need top see, especially if you don’t know the field or property name.

Reflection to the Rescue

if you’re familiar with the .NET Reflection API, you know that you can use it to examine or manipulate any programming object. I decided to use Reflection to display my complex objects by converting them to a nice, easy-to-read string.

Visualizing Complex Objects
Screen shot shows an arbitrary class being displayed as a string. The same code will display other complicated object structures during debugging sessions.

Benefits

If you download my code, compile it into a dll, you can add it to any project. Next time you’re debugging, just use my method to blast your objects into a textbox or the output pane!  You get instant visibility into complex, nested objects.

Nuts and Bolts: Reflection Methods that Make it Happen

I used these methods to generate a string representation of arbitrary objects:

  • GetValue – retrieves the property value for your class instance, using a PropertyInfo object representing the property in question
  • GetFields – I could have used it, but didn’t, because Web Services don’t return objects with fields. But! You can modify my code to do so, if desired.
    • GetProperties – retrieves all properties for any class type. In the example above, it retrieved, for example, ‘ShipToAddress’, ‘Name’, ‘Street1’, Orders (an array) etc.

‘GetProperties’ returns an array of PropertyInfo objects; each PropertyInfo has

  • PropertyType – for example, if the PropertyInfo represents my OrderID, then the PropertyType will be int
  • Name – for example, ‘OrderId’

I also used the following property of the ‘Type’ class:

  • IsArray – for an arbitrary type, tells you whether it is an array. I think it works for Lists as well, but I didn’t test, because, again, web services return arrays by default, and I’m testing Web Services right now.

Take a Look at the Short, Sweet Code!

// Returns a nice string representation of an arbitrary object, useful for debugging
public static string ComplexObjectToString(object someClassInstance, int indentLevel = 0) {
    StringBuilder sbResult = new StringBuilder();
    Type classType = someClassInstance.GetType();
    PropertyInfo[] allProperties = classType.GetProperties(BindingFlags.Public 
                                    | BindingFlags.DeclaredOnly | BindingFlags.Instance);

    //If the current property is an array, loop through each entry and 
    //display the member values, indented 4 characters deeper:
    if (someClassInstance is IEnumerable && !(someClassInstance is string)) {
        foreach (var anItem in (IEnumerable)someClassInstance) {
            if (IsNotCoreType(anItem.GetType()))
                sbResult.AppendFormat("{0}", anItem);
            else {
                string theItem = ComplexObjectToString(anItem, indentLevel + 4);
                sbResult.Append(theItem);
            }
            sbResult.AppendLine("---------------------");
        }
    } else {
        string indentSpaces = new string(' ', indentLevel);
        foreach (PropertyInfo pi in allProperties) {
            //Handle arrays and lists:
            if (IsEnumerable(pi.PropertyType)) {
                object subProperty = pi.GetValue(someClassInstance, null);
                sbResult.AppendLine(pi.Name + " items:");
                foreach (var anItem in (IEnumerable)subProperty) {
                    string subProps = ComplexObjectToString(anItem, indentLevel + 4);
                    sbResult.Append(subProps);
                    sbResult.AppendLine(new string(' ', indentLevel) + "------");
                }
            } else if (IsNotCoreType(pi.PropertyType)) {
                //Handle nested complex objects:
                object subProperty = pi.GetValue(someClassInstance, null);
                if (subProperty != null) {
                    sbResult.AppendFormat("{0}{1}:\n", indentSpaces, pi.Name);
                    string subProps = ComplexObjectToString(subProperty, indentLevel + 4);
                    sbResult.Append(subProps);
                }
            } else {
                if (!_UnwantedProperties.Contains(pi.Name)) {
                    //Handle normal properties, using the Name from the PropertyInfo and 
                    //GetValue to retrieve each successive value
                    object propVal = pi.GetValue(someClassInstance, null);
                    sbResult.AppendFormat("{0}{1}:\t{2}\n", indentSpaces, pi.Name, propVal);
                }
            }
        }
    }
    return sbResult.ToString();
}

public static bool IsEnumerable(Type theType) {
    return theType.IsArray || 
                    ( 
                    theType != typeof(string)
                    &&
                    theType.GetInterfaces().Any(t => t == typeof(IEnumerable))
                    );
}

/// <summary>
/// Determines whether a given object is a 'built-in' class, such as int, 
/// DateTime, string, etc.
/// </summary>
/// <param name="type">A type which might be a 'built-in' class</param>
/// <returns>True when the class needs to have its properties displayed</returns>
public static bool IsNotCoreType(Type type) {
    return !type.IsPrimitive && !type.ToString().StartsWith("System.");
    //return (type != typeof(object) && Type.GetTypeCode(type) == TypeCode.Object);
}

/// <summary>
/// These properties apply to every class, and I don't want to see them:
/// </summary>
private static string[] _UnwantedProperties = new string[] { "ExtensionData", "IsReadOnly", 
                            "IsFixedSize", "IsSynchronized", "Length", 
                            "LongLength", "Rank", "SyncRoot" };

}

Put it to Work and Visualize Your Data!

So, download my sample project. Extract the class ‘Utility’ and modify (it if you want). For example, you might want to see private properties or fields in your output (not there in this version); that would be easy for you to plug in. Now, build a class library; compile it to a dll. Add your dll to your project! Next debug session, use my class on your huge object back and spit-it-out into a textbox or the immediate pane of the debugger. Frustration relieved!

Screen shot shows how to print text to the output pane
You can print your complex object to the output console from the immediate pane, that can be handy when stopped on a breakpoint. In this case, the code that I executed was a Console.WriteLine using the string output from Utility.ComplexObjectToString()

 Future Directions

  1.  If you’re feeling ambitious, you could adapt my technique to create your own fantastically useful Visual Studio Plug-in! http://msdn.microsoft.com/en-us/vstudio/vextend.aspx
  2. You could also consider using the Visual Studio T4 code generator in conjunction with the technique demonstrated here. For example, you could use it to generate label/textbox pairs, on-the-fly, for any class T4 Code generation:. http://msdn.microsoft.com/en-us/library/bb126445.aspx
  3. You could also incorporate it into your logging routines; that would allow you to capture the complete state of an object that might be misbehaving.

Update

I tweaked it some; I enhanced it to handle lists. I also added a universal XML serializer to the project, as part of the download.

Download

Code Link Download:  Visualize Complex Objects. Note: recently revised to expose the functionality as a separate project, built as a dll.

One thought on “Make Debugging Easier with my Complex Object Visualizer

    […] demonstrated some features of Reflection in a previous post, “Make Debugging Easier with My Complex Object Visualizer“. If you read that post, you know that you can get class properties using reflection like […]

Leave a comment