Click here to Skip to main content
15,890,579 members
Articles / Programming Languages / C#
Article

DebugWriter - A simple property value dumper

Rate me:
Please Sign up or sign in to vote.
4.60/5 (11 votes)
5 Sep 20043 min read 50K   956   21   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:

C#
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:

C#
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.

C#
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.

C#
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:

C#
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:

C#
// 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:

C#
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


Written By
Web Developer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralExcellent! Pin
Daniel Liedke30-Oct-09 0:37
professionalDaniel Liedke30-Oct-09 0:37 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.