/// <summary>
        /// Compare an object of type URI
        /// </summary>
        /// <param name="object1"></param>
        /// <param name="object2"></param>
        /// <param name="breadCrumb"></param>
        private void CompareUri(object object1, object object2, string breadCrumb)
        {
            Uri uri1 = object1 as Uri;
              Uri uri2 = object2 as Uri;

              //This should never happen, null check happens one level up
              if (uri1 == null || uri2 == null)
            return;

              if (uri1.OriginalString != uri2.OriginalString) {
            Difference difference = new Difference {
              ExpectedName = ExpectedName,
              ActualName = ActualName,
              PropertyName = breadCrumb,
              Object1Value = NiceString(uri1.OriginalString),
              Object2Value = NiceString(uri2.OriginalString),
              ChildPropertyName = "OriginalString"
            };
            Differences.Add(difference);
              }
        }
 /// <summary>
 /// Compare a timespan struct
 /// </summary>
 /// <param name="object1"></param>
 /// <param name="object2"></param>
 /// <param name="breadCrumb"></param>
 private void CompareTimespan(object object1, object object2, string breadCrumb)
 {
     if (((TimeSpan)object1).Ticks != ((TimeSpan)object2).Ticks) {
     Difference difference = new Difference {
       ExpectedName = ExpectedName,
       ActualName = ActualName,
       PropertyName = breadCrumb,
       Object1Value = ((TimeSpan)object1).Ticks.ToString(CultureInfo.InvariantCulture),
       Object2Value = ((TimeSpan)object1).Ticks.ToString(CultureInfo.InvariantCulture),
       ChildPropertyName = "Ticks"
     };
     Differences.Add(difference);
       }
 }
        /// <summary>
        /// Compare an object of type Type (Runtime type)
        /// </summary>
        /// <param name="object1"></param>
        /// <param name="object2"></param>
        /// <param name="breadCrumb"></param>
        private void CompareType(object object1, object object2, string breadCrumb)
        {
            Type t1 = (Type)object1;
              Type t2 = (Type)object2;

              if (t1.FullName != t2.FullName) {
            Difference difference = new Difference {
              ExpectedName = ExpectedName,
              ActualName = ActualName,
              PropertyName = breadCrumb,
              Object1Value = t1.FullName,
              Object2Value = t2.FullName,
              ChildPropertyName = "FullName"
            };
            Differences.Add(difference);
              }
        }
 /// <summary>
 /// Compare a pointer struct
 /// </summary>
 /// <param name="object1"></param>
 /// <param name="object2"></param>
 /// <param name="breadCrumb"></param>
 private void ComparePointer(object object1, object object2, string breadCrumb)
 {
     if ((object1 is IntPtr && object2 is IntPtr && ((IntPtr)object1) != ((IntPtr)object2))
       || (object1 is UIntPtr && object2 is UIntPtr && ((UIntPtr)object1) != ((UIntPtr)object2))) {
     Difference difference = new Difference {
       ExpectedName = ExpectedName,
       ActualName = ActualName,
       PropertyName = breadCrumb
     };
     Differences.Add(difference);
       }
 }
        /// <summary>
        /// Compare a simple type
        /// </summary>
        /// <param name="object1"></param>
        /// <param name="object2"></param>
        /// <param name="breadCrumb"></param>
        private void CompareSimpleType(object object1, object object2, string breadCrumb)
        {
            //This should never happen, null check happens one level up
              if (object2 == null || object1 == null)
            return;

              IComparable valOne = object1 as IComparable;

              if (valOne == null)
            throw new Exception("Expected value does not implement IComparable");

              if (valOne.CompareTo(object2) != 0) {
            Difference difference = new Difference {
              ExpectedName = ExpectedName,
              ActualName = ActualName,
              PropertyName = breadCrumb,
              Object1Value = object1.ToString(),
              Object2Value = object2.ToString()
            };
            Differences.Add(difference);
              }
        }
        /// <summary>
        /// Compare an integer indexer
        /// </summary>
        /// <param name="info"></param>
        /// <param name="object1"></param>
        /// <param name="object2"></param>
        /// <param name="breadCrumb"></param>
        private void CompareIndexer(PropertyInfo info, object object1, object object2, string breadCrumb)
        {
            string currentCrumb;
              int indexerCount1 = (int)info.ReflectedType.GetProperty("Count").GetGetMethod().Invoke(object1, new object[] { });
              int indexerCount2 = (int)info.ReflectedType.GetProperty("Count").GetGetMethod().Invoke(object2, new object[] { });

              //Indexers must be the same length
              if (indexerCount1 != indexerCount2) {
            currentCrumb = AddBreadCrumb(breadCrumb, info.Name, string.Empty, -1);
            Difference difference = new Difference {
              ExpectedName = ExpectedName,
              ActualName = ActualName,
              PropertyName = currentCrumb,
              Object1Value = indexerCount1.ToString(CultureInfo.InvariantCulture),
              Object2Value = indexerCount2.ToString(CultureInfo.InvariantCulture),
              ChildPropertyName = "Count"
            };
            Differences.Add(difference);

            if (Differences.Count >= MaxDifferences)
              return;
              }

              // Run on indexer
              for (int i = 0; i < indexerCount1; i++) {
            currentCrumb = AddBreadCrumb(breadCrumb, info.Name, string.Empty, i);
            object objectValue1 = info.GetValue(object1, new object[] { i });
            object objectValue2 = info.GetValue(object2, new object[] { i });
            Compare(objectValue1, objectValue2, currentCrumb);

            if (Differences.Count >= MaxDifferences)
              return;
              }
        }
        private void CompareIpEndPoint(object object1, object object2, string breadCrumb)
        {
            IPEndPoint ipEndPoint1 = object1 as IPEndPoint;
              IPEndPoint ipEndPoint2 = object2 as IPEndPoint;

              //Null check happens above
              if (ipEndPoint1 == null || ipEndPoint2 == null)
            return;

              if (ipEndPoint1.Port != ipEndPoint2.Port) {
            Difference difference = new Difference {
              ExpectedName = ExpectedName,
              ActualName = ActualName,
              PropertyName = breadCrumb,
              Object1Value = ipEndPoint1.Port.ToString(CultureInfo.InvariantCulture),
              Object2Value = ipEndPoint2.Port.ToString(CultureInfo.InvariantCulture),
              ChildPropertyName = "Port"
            };
            Differences.Add(difference);
              }

              if (Differences.Count >= MaxDifferences)
            return;

              if (ipEndPoint1.Address.ToString() != ipEndPoint2.Address.ToString()) {
            Difference difference = new Difference {
              ExpectedName = ExpectedName,
              ActualName = ActualName,
              PropertyName = breadCrumb,
              Object1Value = ipEndPoint1.Address.ToString(),
              Object2Value = ipEndPoint2.Address.ToString(),
              ChildPropertyName = "Address"
            };
            Differences.Add(difference);
              }
        }
        /// <summary>
        /// Compare a HashSet
        /// </summary>
        /// <param name="object1"></param>
        /// <param name="object2"></param>
        /// <param name="breadCrumb"></param>
        private void CompareHashSet(object object1, object object2, string breadCrumb)
        {
            try {
            _parents.Add(object1);
            _parents.Add(object2);

            Type t1 = object1.GetType();

            //Get count by reflection since we can't cast it to HashSet<>
            int hashSet1Count = (int)GetPropertyValue(t1, object1, "Count");
            int hashSet2Count = (int)GetPropertyValue(t1, object2, "Count");

            //Objects must be the same length
            if (hashSet1Count != hashSet2Count) {
              Difference difference = new Difference {
            ExpectedName = ExpectedName,
            ActualName = ActualName,
            PropertyName = breadCrumb,
            Object1Value = hashSet1Count.ToString(CultureInfo.InvariantCulture),
            Object2Value = hashSet2Count.ToString(CultureInfo.InvariantCulture),
            ChildPropertyName = "Count"
              };
              Differences.Add(difference);

              if (Differences.Count >= MaxDifferences)
            return;
            }

            //Get enumerators by reflection
            MethodInfo methodInfo = GetMethod(t1, "GetEnumerator");
            IEnumerator enumerator1 = (IEnumerator)methodInfo.Invoke(object1, null);
            IEnumerator enumerator2 = (IEnumerator)methodInfo.Invoke(object2, null);

            int count = 0;

            while (enumerator1.MoveNext() && enumerator2.MoveNext()) {
              string currentBreadCrumb = AddBreadCrumb(breadCrumb, string.Empty, string.Empty, count);

              Compare(enumerator1.Current, enumerator2.Current, currentBreadCrumb);

              if (Differences.Count >= MaxDifferences)
            return;

              count++;
            }
              } finally {
            _parents.Remove(object1);
            _parents.Remove(object2);
              }
        }
        /// <summary>
        /// Compare an array or something that implements IList
        /// </summary>
        /// <param name="object1"></param>
        /// <param name="object2"></param>
        /// <param name="breadCrumb"></param>
        private void CompareIList(object object1, object object2, string breadCrumb)
        {
            IList ilist1 = object1 as IList;
              IList ilist2 = object2 as IList;

              //This should never happen, null check happens one level up
              if (ilist1 == null || ilist2 == null)
            return;

              try {
            _parents.Add(object1);
            _parents.Add(object2);

            //Objects must be the same length
            if (ilist1.Count != ilist2.Count) {
              Difference difference = new Difference {
            ExpectedName = ExpectedName,
            ActualName = ActualName,
            PropertyName = breadCrumb,
            Object1Value = ilist1.Count.ToString(CultureInfo.InvariantCulture),
            Object2Value = ilist2.Count.ToString(CultureInfo.InvariantCulture),
            ChildPropertyName = "Count"
              };
              Differences.Add(difference);

              if (Differences.Count >= MaxDifferences)
            return;
            }

            IEnumerator enumerator1 = ilist1.GetEnumerator();
            IEnumerator enumerator2 = ilist2.GetEnumerator();
            int count = 0;

            while (enumerator1.MoveNext() && enumerator2.MoveNext()) {
              string currentBreadCrumb = AddBreadCrumb(breadCrumb, string.Empty, string.Empty, count);

              Compare(enumerator1.Current, enumerator2.Current, currentBreadCrumb);

              if (Differences.Count >= MaxDifferences)
            return;

              count++;
            }
              } finally {
            _parents.Remove(object1);
            _parents.Remove(object2);
              }
        }
        /// <summary>
        /// Compare an enumeration
        /// </summary>
        /// <param name="object1"></param>
        /// <param name="object2"></param>
        /// <param name="breadCrumb"></param>
        private void CompareEnum(object object1, object object2, string breadCrumb)
        {
            if (object1.ToString() != object2.ToString()) {
            string currentBreadCrumb = AddBreadCrumb(breadCrumb, object1.GetType().Name, string.Empty, -1);

            Difference difference = new Difference {
              ExpectedName = ExpectedName,
              ActualName = ActualName,
              PropertyName = currentBreadCrumb,
              Object1Value = object1.ToString(),
              Object2Value = object2.ToString()
            };
            Differences.Add(difference);
              }
        }
        /// <summary>
        /// Compare two objects
        /// </summary>
        /// <param name="object1">The first object to compare</param>
        /// <param name="object2">The second object to compare</param>
        /// <param name="breadCrumb">Where we are in the object hiearchy</param>
        private void Compare(object object1, object object2, string breadCrumb)
        {
            //If both null return true
              if (object1 == null && object2 == null)
            return;

              //Check if one of them is null
              if (object1 == null) {
            Difference difference = new Difference {
              ExpectedName = ExpectedName,
              ActualName = ActualName,
              PropertyName = breadCrumb,
              Object1Value = "(null)",
              Object2Value = NiceString(object2)
            };
            Differences.Add(difference);
            return;
              }

              if (object2 == null) {
            Difference difference = new Difference {
              ExpectedName = ExpectedName,
              ActualName = ActualName,
              PropertyName = breadCrumb,
              Object1Value = NiceString(object1),
              Object2Value = "(null)"
            };
            Differences.Add(difference);
            return;
              }

              Type t1 = object1.GetType();
              Type t2 = object2.GetType();

              //Objects must be the same type
              if (t1 != t2 && !IgnoreObjectTypes) {
            Difference difference = new Difference {
              ExpectedName = ExpectedName,
              ActualName = ActualName,
              PropertyName = breadCrumb,
              Object1Value = t1.FullName,
              Object2Value = t2.FullName,
              ChildPropertyName = "GetType()",
              MessagePrefix = "Different Types"
            };
            Differences.Add(difference);

            return;
              }

              if (IsUseCustomTypeComparer(t1)) {
            CompareWithCustomComparer(object1, object2, breadCrumb);
              } else if (IsTypeOfType(t1)) {
            CompareType(object1, object2, breadCrumb);
              } else if (IsIPEndPoint(t1)) {
            CompareIpEndPoint(object1, object2, breadCrumb);
              }
            #if !SILVERLIGHT
             else if (IsDataset(t1)) {
            CompareDataset(object1, object2, breadCrumb);
              } else if (IsDataTable(t1)) {
            CompareDataTable(object1, object2, breadCrumb);
              } else if (IsDataRow(t1)) {
            CompareDataRow(object1, object2, breadCrumb);
              }
            #endif
             else if (IsIList(t1)) //This will do arrays, multi-dimensional arrays and generic lists
            {
            CompareIList(object1, object2, breadCrumb);
              } else if (IsHashSet(t1)) {
            CompareHashSet(object1, object2, breadCrumb);
              } else if (IsIDictionary(t1)) {
            CompareIDictionary(object1, object2, breadCrumb);
              } else if (IsEnum(t1)) {
            CompareEnum(object1, object2, breadCrumb);
              } else if (IsPointer(t1)) {
            ComparePointer(object1, object2, breadCrumb);
              } else if (IsUri(t1)) {
            CompareUri(object1, object2, breadCrumb);
              } else if (IsSimpleType(t1)) {
            CompareSimpleType(object1, object2, breadCrumb);
              } else if (IsClass(t1)) {
            CompareClass(object1, object2, breadCrumb);
              } else if (IsTimespan(t1)) {
            CompareTimespan(object1, object2, breadCrumb);
              } else if (IsStruct(t1)) {
            CompareStruct(object1, object2, breadCrumb);
              } else {
            throw new NotSupportedException("Cannot compare object of type " + t1.Name);
              }
        }
        /// <summary>
        /// Compare all rows in a data table
        /// </summary>
        /// <param name="object1"></param>
        /// <param name="object2"></param>
        /// <param name="breadCrumb"></param>
        private void CompareDataTable(object object1, object object2, string breadCrumb)
        {
            DataTable dataTable1 = object1 as DataTable;
              DataTable dataTable2 = object2 as DataTable;

              //This should never happen, null check happens one level up
              if (dataTable1 == null || dataTable2 == null)
            return;

              //Only compare specific table names
              if (ElementsToInclude.Count > 0 && !ElementsToInclude.Contains(dataTable1.TableName))
            return;

              //If we should ignore it, skip it
              if (ElementsToInclude.Count == 0 && ElementsToIgnore.Contains(dataTable1.TableName))
            return;

              //There must be the same amount of rows in the datatable
              if (dataTable1.Rows.Count != dataTable2.Rows.Count) {
            Difference difference = new Difference {
              ExpectedName = ExpectedName,
              ActualName = ActualName,
              PropertyName = breadCrumb,
              Object1Value = dataTable1.Rows.Count.ToString(CultureInfo.InvariantCulture),
              Object2Value = dataTable2.Rows.Count.ToString(CultureInfo.InvariantCulture),
              ChildPropertyName = "Rows.Count"
            };
            Differences.Add(difference);

            if (Differences.Count >= MaxDifferences)
              return;
              }

              //There must be the same amount of columns in the datatable
              if (dataTable1.Columns.Count != dataTable2.Columns.Count) {
            Difference difference = new Difference {
              ExpectedName = ExpectedName,
              ActualName = ActualName,
              PropertyName = breadCrumb,
              Object1Value = dataTable1.Columns.Count.ToString(CultureInfo.InvariantCulture),
              Object2Value = dataTable2.Columns.Count.ToString(CultureInfo.InvariantCulture),
              ChildPropertyName = "Columns.Count"
            };
            Differences.Add(difference);

            if (Differences.Count >= MaxDifferences)
              return;
              }

              for (int i = 0; i < Math.Min(dataTable1.Rows.Count, dataTable2.Rows.Count); i++) {
            string currentBreadCrumb = AddBreadCrumb(breadCrumb, "Rows", string.Empty, i);

            CompareDataRow(dataTable1.Rows[i], dataTable2.Rows[i], currentBreadCrumb);

            if (Differences.Count >= MaxDifferences)
              return;
              }
        }
        /// <summary>
        /// Compare all tables and all rows in all tables
        /// </summary>
        /// <param name="object1"></param>
        /// <param name="object2"></param>
        /// <param name="breadCrumb"></param>
        private void CompareDataset(object object1, object object2, string breadCrumb)
        {
            DataSet dataSet1 = object1 as DataSet;
              DataSet dataSet2 = object2 as DataSet;

              //This should never happen, null check happens one level up
              if (dataSet1 == null || dataSet2 == null)
            return;

              //There must be the same amount of tables in the dataset
              if (dataSet1.Tables.Count != dataSet2.Tables.Count) {
            Difference difference = new Difference {
              ExpectedName = ExpectedName,
              ActualName = ActualName,
              PropertyName = breadCrumb,
              Object1Value = dataSet1.Tables.Count.ToString(CultureInfo.InvariantCulture),
              Object2Value = dataSet2.Tables.Count.ToString(CultureInfo.InvariantCulture),
              ChildPropertyName = "Tables.Count"
            };
            Differences.Add(difference);

            if (Differences.Count >= MaxDifferences)
              return;
              }

              for (int i = 0; i < Math.Min(dataSet1.Tables.Count, dataSet2.Tables.Count); i++) {
            string currentBreadCrumb = AddBreadCrumb(breadCrumb, "Tables", string.Empty, dataSet1.Tables[i].TableName);

            CompareDataTable(dataSet1.Tables[i], dataSet2.Tables[i], currentBreadCrumb);

            if (Differences.Count >= MaxDifferences)
              return;
              }
        }
        /// <summary>
        /// Compare all columns in a data row
        /// </summary>
        /// <param name="object1"></param>
        /// <param name="object2"></param>
        /// <param name="breadCrumb"></param>
        private void CompareDataRow(object object1, object object2, string breadCrumb)
        {
            DataRow dataRow1 = object1 as DataRow;
              DataRow dataRow2 = object2 as DataRow;

              //This should never happen, null check happens one level up
              if (dataRow1 == null || dataRow2 == null)
            return;

              for (int i = 0; i < dataRow1.Table.Columns.Count; i++) {
            //Only compare specific column names
            if (ElementsToInclude.Count > 0 && !ElementsToInclude.Contains(dataRow1.Table.Columns[i].ColumnName))
              continue;

            //If we should ignore it, skip it
            if (ElementsToInclude.Count == 0 && ElementsToIgnore.Contains(dataRow1.Table.Columns[i].ColumnName))
              continue;

            //If we should ignore read only, skip it
            if (!CompareReadOnly && dataRow1.Table.Columns[i].ReadOnly)
              continue;

            //Both are null
            if (dataRow1.IsNull(i) && dataRow2.IsNull(i))
              continue;

            string currentBreadCrumb = AddBreadCrumb(breadCrumb, string.Empty, string.Empty, dataRow1.Table.Columns[i].ColumnName);

            //Check if one of them is null
            if (dataRow1.IsNull(i)) {
              Difference difference = new Difference {
            ExpectedName = ExpectedName,
            ActualName = ActualName,
            PropertyName = currentBreadCrumb,
            Object1Value = "(null)",
            Object2Value = NiceString(object2)
              };
              Differences.Add(difference);
              return;
            }

            if (dataRow2.IsNull(i)) {
              Difference difference = new Difference {
            ExpectedName = ExpectedName,
            ActualName = ActualName,
            PropertyName = currentBreadCrumb,
            Object1Value = NiceString(object1),
            Object2Value = "(null)"
              };
              Differences.Add(difference);
              return;
            }

            Compare(dataRow1[i], dataRow2[i], currentBreadCrumb);

            if (Differences.Count >= MaxDifferences)
              return;
              }
        }