This class compares 2 objects with eachother (custom objects)
With the possibility to do a deep compare.
You can off course implement a IComparable interface and do it manually,
but i find it a little more useful to do it with an utility, so that i can use it on no-mather which class.
If you have comments or can make it better, don't hesitate to post a comment.
There are probably other ways to do it, but i find this method a fast-enough method for most of the applications.
1: /// <summary>
2: /// Compares 2 objects with each other.
3: /// </summary>
4: public class Comparer
5: {
6: #region Declarations
7:
8: private static readonly Type _stringType;
9: private delegate int ILCompareHandler(object o1, object o2);
10: private static ILCompareHandler _ILComparer;
11:
12: #endregion
13:
14: #region Static Constructor
15:
16: static Comparer()
17: {
18: _stringType = typeof(string);
19: }
20:
21: #endregion
22:
23: #region Public Methods
24:
25: /// <summary>
26: /// Checks wether 2 objects are equal.
27: /// </summary>
28: /// <param name="o1">Object 1</param>
29: /// <param name="o2">Object 2</param>
30: /// <param name="valueTypesOnly">Only check the value-types? (false = also check the associated-classes)</param>
31: /// <returns>Wether the 2 objects are equal in VALUE</returns>
32: public static bool CompareTo(object o1, object o2, bool valueTypesOnly)
33: {
34: Type to1 = o1.GetType();
35: Type to2 = o1.GetType();
36: if (to1.FullName.CompareTo(to2.FullName) != 0)
37: return false;
38: PropertyInfo[] props = to1.GetProperties();
39: // Types are equal, compare each value of each object
40: if (valueTypesOnly)
41: {
42: return TestOnValuesOnly(props, o1, o2);
43: }
44: else
45: {
46: bool innervaluesAreEqual = TestRecursiveValues(props, o1, o2, false);
47: return innervaluesAreEqual;
48: }
49: }
50:
51: #endregion
52:
53: #region Private Methods
54:
55: private static bool TestRecursiveValues(PropertyInfo[] props, object o1, object o2, bool inside)
56: {
57: if (o1 == null && o2 == null)
58: return true;
59: if ((o1 != null && o2 == null) || (o1 == null && o2 != null))
60: return false;
61:
62: bool classOK = true;
63: for (int i = 0; i < props.Length; i++)
64: {
65: PropertyInfo pi = props[i];
66: if (pi.PropertyType.IsClass && pi.PropertyType != _stringType)
67: {
68: object object1 = pi.GetValue(o1, null);
69: object object2 = pi.GetValue(o2, null);
70: if (object1 is IEnumerable)
71: {
72: classOK = false;
73: IEnumerator enumerator1 = (object1 as IEnumerable).GetEnumerator();
74: IEnumerator enumerator2 = (object2 as IEnumerable).GetEnumerator();
75: while (enumerator1.MoveNext() && enumerator2.MoveNext())
76: {
77: classOK = TestRecursiveValues(enumerator1.Current.GetType().GetProperties(), enumerator1.Current, enumerator2.Current, true);
78: if (!classOK)
79: return classOK;
80: }
81: if (!classOK)
82: return classOK;
83: }
84: else
85: {
86: classOK = TestRecursiveValues(pi.PropertyType.GetProperties(), object1, object2, true);
87: if (!classOK)
88: return classOK;
89: }
90: }
91: else
92: {
93: if (inside)
94: {
95: if (o1.GetType().IsValueType || o1 is string)
96: {
97: if (o1.GetType().IsValueType)
98: if (ILCompareValueTypes(o1, o2) != 0)
99: return false;
100: if (o1 is string && o1 != o2)
101: return false;
102: }
103: else
104: {
105: // We have an inside class , check our values
106: classOK = TestOnValuesOnly(props, o1, o2);
107: if (!classOK)
108: return classOK;
109: }
110: }
111: }
112: }
113: return classOK;
114: }
115:
116: private static bool TestOnValuesOnly(PropertyInfo[] props, object o1, object o2)
117: {
118: if (o1 == null && o2 == null)
119: return true;
120: if ((o1 != null && o2 == null) || (o1 == null && o2 != null))
121: return false;
122: for (int i = 0; i < props.Length; i++)
123: {
124: PropertyInfo pi = props[i];
125: if (pi.PropertyType.IsValueType && !pi.PropertyType.IsArray)
126: {
127: object so1 = pi.GetValue(o1, null) ?? string.Empty;
128: object so2 = pi.GetValue(o2, null) ?? string.Empty;
129:
130: if (ILCompareValueTypes(so1, so2) != 0)
131: return false;
132: }
133: if (pi.PropertyType == _stringType)
134: {
135: object so1 = pi.GetValue(o1, null) as string ?? string.Empty;
136: object so2 = pi.GetValue(o2, null) as string ?? string.Empty;
137:
138: if (so1 != so2)
139: return false;
140: }
141: }
142: return true;
143: }
144:
145: private static int ILCompareValueTypes(object value1, object value2)
146: {
147: if (_ILComparer == null)
148: {
149: Type[] _argTypes = { typeof(object), typeof(object) };
150:
151: DynamicMethod dynam =
152: new DynamicMethod(
153: "ILComparer",
154: typeof(int),
155: _argTypes,
156: typeof(Comparer));
157: ILGenerator il = dynam.GetILGenerator();
158:
159: il.Emit(OpCodes.Ldarg_0);
160: il.Emit(OpCodes.Ldarg_1);
161: il.Emit(OpCodes.Ceq);
162: il.Emit(OpCodes.Ret);
163:
164: _ILComparer = dynam.CreateDelegate(typeof(ILCompareHandler)) as ILCompareHandler;
165: }
166: return _ILComparer(value1, value2);
167: }
168:
169: #endregion
170: }
2 comments:
Hi Filip,
Nice solution for comparing objects.
Take into account that using reflection decreases performance.
For comparing only a few values, there is no problem but when comparing multiple values, it can give a big difference. When performance is critical or the number of comparisons is high, it is better to implement the IComparable interface as you mentioned.
Greetz,
Geert
Hello Geert,
Thanks for your input,
Indeed for a lot of values it could be wise to use the IComparable interface, altough, this method is pretty fast, the IL makes a lot good.
I use this class sometimes as base-class, with also the IComparable implemented, that way I just have to inherit and call the CompareTo without writing my own compares (call it responsable lazy ;)) when it's to slow, i override the CompareTo and to it manually.
For the most common simple domain objects it's very very very handy, and saves a lot of time.
Greets,
Filip
Post a Comment