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);
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);
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:
public static void Write(object o, StringCollection propertyNames,
string delimTop, string delimBottom,
string delimBetween, bool messageBox)
{
Type typeToWrite = o.GetType();
ArrayList properties = new ArrayList();
PropertyInfo property;
foreach(string propertyName in propertyNames)
{
property = typeToWrite.GetProperty(propertyName);
if(property != null)
{
properties.Add(property);
}
}
WriteProperties(o, properties, delimTop,
delimBottom, delimBetween, messageBox);
}
public void Write()
{
if(PropertyNames.Count > 0)
{
Write(_objectToWrite, _propertyNames,
_objectDelimiterTop, _objectDelimiterBottom,
_propertyDelimiter, _showInMessageBox);
}
else
{
Write(_objectToWrite, _objectDelimiterTop, _objectDelimiterBottom,
_propertyDelimiter, _showInMessageBox);
}
}
private static void WriteProperties(object o, ArrayList properties,
string delimTop, string delimBottom,
string delimBetween, bool messageBox)
{
if(properties.Count < 1) return;
StringBuilder output = new StringBuilder();
output.Append(delimTop);
output.Append("\n");
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)
{
output.Append("CALL FAILED - ");
output.Append(ex.InnerException.Message);
}
catch(Exception ex)
{
output.Append("CALL FAILED - ");
output.Append(ex.Message);
}
if(delimBetween != null && delimBetween.Length > 0
&& propertyCount < totalProperties)
{
output.Append(delimBetween);
output.Append("\n");
}
}
output.Append(delimBottom);
output.Append("\n");
string finalResult = output.ToString();
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
.