Thursday, February 8, 2007

Object comparer

With this post i want to show a compare for objects.
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: }

Thursday, February 1, 2007

C# ReadOnly class

This class is meant for variables in C# that must be readonly.
Offcourse you can use the "readonly" keyword of C#, but you can only initialize it in the constructor of a type.

This little class makes it possible to initialize it in the constructor and assign it once somewhere else. The class is generic and supports implicit casting to the generic type.

usage:


   1: int x = 0; 
   2: ReadOnly myInt = new ReadOnly(); 
   3: myInt = 10; 
   4: x = myInt; 
   5: myInt = 15; // throws an exception 
   6: Console.WriteLine("val: {0}", x); 


The class definition:


   1: using System;
   2: using System.Collections.Generic;
   3: using System.Text;
   4:  
   5: namespace Whizzo
   6: {
   7:     public struct ReadOnly
   8:     {
   9:         private static ReadOnlyVar? _readOnlyVar;
  10:  
  11:         #region Constructor
  12:  
  13:         public ReadOnly(T var)
  14:         {
  15:             _readOnlyVar = new ReadOnlyVar(var);
  16:         }
  17:  
  18:         #endregion
  19:  
  20:         #region Implicit casts
  21:  
  22:         /// 
  23:         /// User-defined conversion from T to ReadOnly
  24:         /// 
  25:         /// type to cast to
  26:         /// a new readonly variable, filled in
  27:         public static implicit operator ReadOnly(T var)
  28:         {
  29:             if (_readOnlyVar == null)
  30:                 return new ReadOnly(var);
  31:             throw new InvalidOperationException("Variable is read-only, and already assigned.");
  32:         }
  33:  
  34:         /// 
  35:         /// User-defined conversion from ReadOnly to T
  36:         /// 
  37:         /// 
  38:         /// 
  39:         public static implicit operator T(ReadOnly var)
  40:         {
  41:             return _readOnlyVar.HasValue ? _readOnlyVar.Value.Var : default(T);
  42:         }
  43:  
  44:         #endregion
  45:  
  46:         #region Overrides
  47:  
  48:         public override string ToString()
  49:         {
  50:             return _readOnlyVar.HasValue ? _readOnlyVar.Value.Var.ToString() : "No value";
  51:         }
  52:  
  53:         #endregion
  54:  
  55:         #region Nested Types
  56:  
  57:         private struct ReadOnlyVar
  58:         {
  59:             private readonly T _var;
  60:  
  61:             public ReadOnlyVar(T var)
  62:             {
  63:                 _var = var;
  64:             }
  65:  
  66:             public T Var
  67:             {
  68:                 get { return _var; }
  69:             }
  70:         }
  71:  
  72:         #endregion
  73:     }
  74: }


Just copy the code into a ReadOnly.cs file or something like that, and start using ;)
There are many different ways to make something like this, but i find this a handy way.