Uses 64bit to 32bit hash Mapping for primitive, Value, and Reference types
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.Serialization.Formatters.Binary; using System.Security; [Serializable] public class MapComparer<T> : IEqualityComparer<T> { private static Mapping64BitToHash32Bit _hash; private static Type _type = typeof(T); private static readonly BinaryFormatter _bf = new BinaryFormatter(); private static readonly MemoryStream _ms = new MemoryStream(); private readonly bool _isPrimitive; public MapComparer() { _isPrimitive = IsPrimitive(); _hash = new Mapping64BitToHash32Bit(); _type = typeof(T); } public bool Equals(T x, T y) { if (x == null || y == null) return false; if (_isPrimitive) return x.Equals(y); var xb = GetBytesObject(x, _type); var yb = GetBytesObject(y, _type); if (xb.Length != yb.Length) return false; return xb.Compare(yb); } public int GetHashCode(T obj) { var buf = GetBytesObject(obj, _type); return _hash.ComputeHash(buf).ToInt(); } private static byte[] GetBytesObjectSerial(object value) { if (!_type.IsSerializable) return GetBytesObjectR(value); _ms.SetLength(0); _bf.Serialize(_ms, value); return _ms.ToArray().SubArray(0, (int) _ms.Length); } [SecurityCritical] private static byte[] GetBytesObjectR(object data) { var result = new List<byte>(); var type = data.GetType(); IEnumerable<FieldInfo> tFields = type.GetFields(); foreach (var fieldInfo in tFields) { var value = fieldInfo.GetValue(data); var lt = value.GetType(); var buf = GetBytesObject(value, lt); result.AddRange(buf); } var p = type.GetNestedTypes(); if (p.Length > 0) foreach (var t in p) { var nFields = t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static).ToArray(); if (nFields.Length > 0) if (!nFields[0].IsStatic) return result.ToArray(); foreach (var fieldInfo in nFields) { var value = fieldInfo.GetValue(null); var lt = value.GetType(); var buf = GetBytesObject(value, lt); result.AddRange(buf); } } return result.ToArray(); } [SecurityCritical] private static byte[] GetBytesObject(object obj, Type ltype) { switch (ltype.Name.Trim('[', ']')) { case "Byte": return !ltype.IsArray ? new[] {(byte) obj} : (byte[]) obj; case "Boolean": return !ltype.IsArray ? ((bool) obj).GetBytes() : ((bool[]) obj).GetBytes(); case "SByte": return !ltype.IsArray ? ((sbyte) obj).GetBytes() : ((sbyte[]) obj).GetBytes(); case "Char": return !ltype.IsArray ? ((char) obj).GetBytes() : ((char[]) obj).GetBytes(); case "Int16": return !ltype.IsArray ? ((short) obj).GetBytes() : ((short[]) obj).GetBytes(); case "UInt16": return !ltype.IsArray ? ((ushort) obj).GetBytes() : ((ushort[]) obj).GetBytes(); case "Int32": return !ltype.IsArray ? ((int) obj).GetBytes() : ((int[]) obj).GetBytes(); case "UInt32": return !ltype.IsArray ? ((uint) obj).GetBytes() : ((uint[]) obj).GetBytes(); case "Int64": return !ltype.IsArray ? ((long) obj).GetBytes() : ((long[]) obj).GetBytes(); case "UInt64": return !ltype.IsArray ? ((ulong) obj).GetBytes() : ((ulong[]) obj).GetBytes(); case "Single": return !ltype.IsArray ? ((float) obj).GetBytes() : ((float[]) obj).GetBytes(); case "Double": return !ltype.IsArray ? ((double) obj).GetBytes() : ((double[]) obj).GetBytes(); case "String": return !ltype.IsArray ? ((string) obj).GetBytes() : ((string[]) obj).GetBytes(); case "Decimal": return !ltype.IsArray ? ((decimal) obj).GetBytes() : ((decimal[]) obj).GetBytes(); case "DateTime": return !ltype.IsArray ? ((DateTime) obj).GetBytes() : ((DateTime[]) obj).GetBytes(); } return GetBytesObjectSerial(obj); } private bool IsPrimitive() { switch (Type.GetTypeCode(_type)) { case TypeCode.Boolean: case TypeCode.Char: case TypeCode.SByte: case TypeCode.Byte: case TypeCode.Int16: case TypeCode.UInt16: case TypeCode.Int32: case TypeCode.UInt32: case TypeCode.Single: case TypeCode.String: case TypeCode.Decimal: case TypeCode.DateTime: case TypeCode.Int64: case TypeCode.UInt64: case TypeCode.Double: return true; default: return false; } } }