Thursday, December 2, 2010

Object Comparison

Overview


So this is a really quick example of how you can compare two objects to see if they are the same or not using reflection.  This allows you to compare 2 objects (see limitations below) without having to modify them.

Basically what this example does it loop through the properties of two objects and return a boolean if they are the same or not.  If they're not the same, an output list shows what values differ.

Sample Code

The basic code to do the comparison is as follows:

public static bool IsSame(object a, object b, out string differences)
        {
            differences = string.Empty;
            bool isSame = true;

            if (a.GetType().Name != b.GetType().Name)
            {
                isSame = false;
                differences = "Type does not match!";
            }        

            PropertyInfo[] propertyInfos; //array to store object properties
          
            //query the object to get the list of properties contained in this object
            propertyInfos = a.GetType().GetProperties();
          
            // sort properties by name
            Array.Sort(propertyInfos,
                    delegate(PropertyInfo propertyInfo1, PropertyInfo propertyInfo2)
                    { return propertyInfo1.Name.CompareTo(propertyInfo2.Name); });

            //loop through the properties to compare to object b.
            foreach (PropertyInfo propertyInfo in propertyInfos)
            {
                string aValue, bValue;
                aValue = propertyInfo.GetValue(a, null).ToString();
                bValue = propertyInfo.GetValue(b, null).ToString();

                if (aValue == bValue)
                {

                }
                else
                {
                    isSame = false;
                    differences += string.Format("{0}: {1}, {2}" + Environment.NewLine, propertyInfo.Name, aValue, bValue);
                }
            }

            return isSame;
        }

Now, there is a problem with this code... a property is considered a public member that has a "GET".  So in the example below... I get a "true" that the objects are the same but in reality the public "BIO" isn't getting checked because its not a true "property" as it doesn't have a GET... its more of a variable.  I did this intentionally to show some considerations for using reflection to compare an object.

So here is my definition for my "Person" object in the example:


    public sealed class Person
    {
        private string m_FirstName;
        private string m_LastName;
        private int m_Age;

        public string FirstName { get { return m_FirstName; } set { m_FirstName = value; } }
        public string LastName { get { return m_FirstName; } set { m_LastName = value; } }
        public int Age { get { return m_Age; } set { m_Age = value; } }
        public string Bio;

        public Person()
        {

        }
    }

And this is the actual test method:


Person a = new Person();
            Person b = new Person();

            a.FirstName = "John";
            a.LastName = "Doe";
            a.Age = 55;
            a.Bio = "John likes tacos.";

            b.FirstName = "John";
            b.LastName = "Doe";
            b.Age = 55;
            b.Bio = "John likes chips.";

            /*a.FirstName = "John";
            a.LastName = "Doe";
            a.Age = 55;
            a.Bio = "John likes tacos.";

            b.FirstName = "Jane";
            b.LastName = "Doe";
            b.Age = 22;
            b.Bio  = "Jane likes apples.";*/

            bool result;
            string differences;

            result = Compare.IsSame(a, b, out differences);

            MessageBox.Show(differences, string.Format("Are these the same? {0}", result.ToString()));


Notice  I commented out a snippet that you can uncomment and run to show the differences between the objects.

Alternatives


The pro to this comparison is that I can determine what values were different.  The con (as I've shown) is that this needs more fleshing out because public members like "Bio" didn't get pulled in and checked... and it was different.  Also, if I had a property that returns an array... I'm not checking the array and the ToString() would likely return just the type of the array/collection and not compare the individual members.

Some alternatives I can think of off the top of my head... I could convert both objects to binary and if they were the same binary output... the binaries would be the same (or the XML).  But it wouldn't tell me what changed.

I could also write an override function or override the "ToString" to output all the values such that I could compare the ToString results of the two objects.  In that same vein I could implement IComparable to allow the CompareTo function in order to determine if my objects are the same or not.

I guess my point there is that this is one potential way of solving the problem of comparing objects but there are others as well.  Which you use depends on your needs.