Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

DebugWriter - A simple property value dumper

0.00/5 (No votes)
5 Sep 2004 1  
A simple class to help dump property values at design-time.

Sample Image - DebugWriter.jpg

Introduction

I'm probably not alone in this, but I often find myself writing code like this to find out the state of an object during development:

Console.WriteLine("EmployeeName: " + emp.Name);
Console.WriteLine("SSN: " + emp.SSN);
Console.WriteLine("Phone: " + emp.Phone);
Console.WriteLine("Address: " + emp.Address);
// and on and on

If it's in a loop, I'll sometimes add beginning and/or ending delimiters to help visually break up the output:

foreach(Employee emp in Employees)
{
    Console.WriteLine("*****BEGIN**********");
    Console.WriteLine("EmployeeName: " + emp.Name);
    Console.WriteLine("SSN: " + emp.SSN);
    Console.WriteLine("Phone: " + emp.Phone);
    Console.WriteLine("Address: " + emp.Address);
    // etc.

    Console.WriteLine("********************");
}

Needless to say, this kind of thing gets tedious very quickly. So, I decided to spend a few keystrokes and write a simple class that will do it for me.

Using the code

The resulting class, called DebugWriter, is quite simple to use and can save you quite a bit of typing. The most basic use of the DebugWriter class is to call its static Write method, passing in an object as a parameter. Here is the call and the result (on my machine) when passed a System.Data.SqlClient.SqlConnection object:

DebugWriter.Write(new SqlConnection());

--Output--

ConnectionString: 
ConnectionTimeout: 15
Database: 
DataSource: 
PacketSize: 8192
WorkstationId: CHARLIE
ServerVersion: CALL FAILED - Invalid operation. 
               The connection is closed.State: Closed
Site: NULL
Container: NULL

With the previous call, you don't have much control over the output. To see what I mean, pass an instance of System.Windows.Forms.Form and stand back as you get barraged with over 100 properties. I could be wrong, but I'm guessing you don't want to sift through all that to find the few you are interested in. That's why Write has an overload that accepts a params string[] argument that consists of the names of the properties you want to see.

Form frm = new Form();
DebugWriter.Write(frm, "Text", "BackColor", "Location", "Size");

There are also a couple of simple formatting options available. These can be passed in an overload of DebugWriter.Write, but it starts to get a little clumsy. This is where creating an instance of DebugWriter comes in handy. It also allows you to add property names one at a time, if you so wish.

Form1 frm = new Form1();

DebugWriter writer = new DebugWriter(frm);
writer.ObjectDelimiterTop = "***BEGIN***";
writer.ObjectDelimiterBottom = "****END****";
writer.PropertyDelimiter = "---";
writer.PropertyNames.Add("Text");
writer.PropertyNames.Add("Font");
writer.PropertyNames.Add("Size");
writer.Write();
--Output--


***BEGIN***
Text: Form1
---
Font: [Font: Name=Microsoft Sans Serif, Size=8.25, 
       Units=3, GdiCharSet=0, GdiVerticalFont=False]
---
Size: {Width=300, Height=300}
****END****

There's also a ShowInMessageBox property that determines if the output is displayed in a message box rather than being written to registered debug listeners. It is false by default.

That's about all there is to using this class. If you are interested, here is how I did it.

The DebugWriter Class

Here are all of the namespaces I use:

using System;
using System.Text;
using System.Collections;
using System.Collections.Specialized;
using System.Reflection;
using System.Diagnostics;
using System.Windows.Forms;

And here is the relatively self-explanatory code:

// One of the overloads of the static Write method

public static void Write(object o, StringCollection propertyNames, 
                         string delimTop, string delimBottom, 
                         string delimBetween, bool messageBox)
{
    // Get the appropriate Type object

    Type typeToWrite = o.GetType();

    // If this were for use in production code, I may have written

    // a collection class to avoid casting,

    // but I won't worry about it here.

    ArrayList properties = new ArrayList();

    // Used in foreach loop

    PropertyInfo property;

    foreach(string propertyName in propertyNames)
    {
        property = typeToWrite.GetProperty(propertyName);
        
        // property will be null if propertyName doesn't match exactly

        if(property != null)
        {
            properties.Add(property);
        }
    }
    
    // Call the method that actually writes the values

    WriteProperties(o, properties, delimTop, 
               delimBottom, delimBetween, messageBox);
}

// This instance method simply calls the appropriate static version

public void Write()
{
    if(PropertyNames.Count > 0) // Property names have been specified

    {
         // _objectToWrite, _propertyNames, etc.

         // are private fields of this class

        Write(_objectToWrite, _propertyNames, 
            _objectDelimiterTop, _objectDelimiterBottom, 
            _propertyDelimiter, _showInMessageBox);
    }
    else // No property names specified, so we'll get 'em all

    {
        Write(_objectToWrite, _objectDelimiterTop, _objectDelimiterBottom, 
            _propertyDelimiter, _showInMessageBox);
    }
}

// And here's the method produces the output, in all its glory

private static void WriteProperties(object o, ArrayList properties, 
          string delimTop, string delimBottom, 
          string delimBetween, bool messageBox)
{
    // Make sure there's something to do

    if(properties.Count < 1) return;

    // This is... well, the output

    StringBuilder output = new StringBuilder();

    output.Append(delimTop);
    output.Append("\n");

    // Used in foreach loop

    PropertyInfo property;

    int totalProperties = properties.Count;
    int propertyCount = 0;

    foreach(object prop in properties)
    {
        propertyCount++;

        property = (PropertyInfo)prop;

        output.Append(property.Name);
        output.Append(": ");

        try
        {
            object oval = property.GetValue(o, null);
            string val = oval == null ? "NULL" : oval.ToString();
                    
            output.Append(val);
            output.Append("\n");
        }
        catch(TargetInvocationException ex)
        {
            // An exception was thrown by the property's

            // get method

            output.Append("CALL FAILED - ");
            output.Append(ex.InnerException.Message);
        }
        catch(Exception ex)
        {
            output.Append("CALL FAILED - ");
            output.Append(ex.Message);
        }
            
        // We don't want the parameter delimiter

        // to print after the last property

        if(delimBetween != null && delimBetween.Length > 0 
            && propertyCount < totalProperties)
        {
            output.Append(delimBetween);
            output.Append("\n");
        }            
    }

    output.Append(delimBottom);
    output.Append("\n");

    string finalResult = output.ToString();

    // This is what we came for

    if(messageBox)
    {
        MessageBox.Show(finalResult, o.GetType().Name);
    }
    else
    {
        Debug.WriteLine(finalResult);
    }
}

Possible Improvements

There is currently no attempt to print the values of properties recursively. For example, if DebugWriter is passed an Employee object which has a Supervisor property consisting of another Employee object, DebugWriter simply calls ToString on the returned Employee, rather than printing its properties as well. That could certainly be introduced, but it becomes much more complicated when you have to worry about circular references. As in:

emp.Supervisor == super.Subordinate

Some other possible improvements would be additional formatting options, such as a user-definable name/value separator, instead of the ": " that is hard-coded now, or user-definable labels to be printed instead of property names.

The Demo Project

The included demo project is about as simple as it gets. When 'Do It!' is clicked, a new instance of SampleClass (my class-naming talents are shining through again) is passed, along with the options specified on the form, to the static Write method of DebugWriter.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here