Friday, December 11, 2009

Reflecting Classes and content

Yesterday someone asked for a way to print out all the contents of a class including the values that were current assigned.  This initially seemed like an easy thing to do, but turned out to be a little more complicated that expected.

The Easy Way

First thing we need to understand about .NET classes is that they are all inherited from a base type called Object.  This base object has a handy little method called ToString(), which will return you a string representation of that object.  You would have used this dozens of times in your code.

int X = 32;
Console.WriteLine("The value of X is:” + X.ToString());

image

This returns “The value of X is:32” which is fine for simple objects like int, float, DateTime but what about our custom classes?.  If you coded up something like the following:

public class SomeObject
{
    public string Firstname { get; set; }
    public string Surname { get; set; }
}

Now you’re test code would look like this:

SomeObject someObject = new SomeObject();
someObject.Firstname = "Tiberius";
someObject.Surname = "Percinus";
Console.WriteLine("The value of someObjectis: " + someObject.ToString());

image

This returns “The value of someObject is: UnitTests.ReflectionExample.SomeObject” which is not really helpful at all.  The normal way around this is to create an override on the ToString() method which returns a more meaningful result.

    public class SomeObject
    {
        public string Firstname { get; set; }
        public string Surname { get; set; }

        /// <summary>
        /// Local implementation of ToString based on class members
        /// </summary>
        public override String ToString()
        {
            StringBuilder sbuffer = new StringBuilder();
            sbuffer.Append("{");
            sbuffer.AppendFormat("Firstname = {0}, ", this.Firstname);
            sbuffer.AppendFormat("Surname = {0}, ", this.Surname);
            sbuffer.Append(" }");
            return sbuffer.ToString();
        }
    }

With this we create a large string with all of the local values returned in a meaningful way.

image 

This returns: “The value of someObject is: {Firstname = Tiberius, Surname = Percinus,  }”.

The Fancy Way

“OK”, I was told, “But this object has only two data items what happens if I’ve got 180?”.  Which is a very valid question.  The best answer to this is to use a technique called “Reflection” which allows you to crack open a class to access its values.

    using System.Reflection;

    public class SomeObject
    {
        public string Firstname { get; set; }
        public string Surname { get; set; }
         //  ……  assume another 180 properties are here ……  //
        public string LastProperty { get; set; }

        /// <summary>
        /// Local implementation of ToString based on reflection
        /// </summary>
        public override String ToString()
        {
            StringBuilder sbuffer = new StringBuilder();
            sbuffer.Append("{");
            PropertyInfo[] properties = this.GetType().GetProperties();
            foreach (PropertyInfo prop in properties)
            {
                sbuffer.AppendFormat("{0} = {1}, ", prop.Name, prop.GetValue(this, null));
            }
            sbuffer.Append(" }");
            return sbuffer.ToString();
        }
    }

Using the foreach loop and the GetType().GetProperties() methods we get an array of all properties in the class.  Then we just have string builder join them all together.

image

This will now return: “The value of someObject is: {Firstname = Tiberius, Surname = Percinus, … and on for all the properties … , LastProperty = ,  }”.

So why not do this all the time? Well the simple reason is that reflection adds a little more load on the processor, so for the majority of cases it’s best to stick to “the easy way”, unless you have a very, very large set of properties.