using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; namespace Unosquare.Swan.Components { /// /// Represents a quick object comparer using the public properties of an object /// or the public members in a structure. /// public static class ObjectComparer { /// /// Compare if two variables of the same type are equal. /// /// The type of objects to compare. /// The left. /// The right. /// true if the variables are equal; otherwise, false. public static Boolean AreEqual(T left, T right) => AreEqual(left, right, typeof(T)); /// /// Compare if two variables of the same type are equal. /// /// The left. /// The right. /// Type of the target. /// /// true if the variables are equal; otherwise, false. /// /// targetType. public static Boolean AreEqual(Object left, Object right, Type targetType) { if(targetType == null) { throw new ArgumentNullException(nameof(targetType)); } return Definitions.BasicTypesInfo.ContainsKey(targetType) ? Equals(left, right) : targetType.IsValueType() || targetType.IsArray ? AreStructsEqual(left, right, targetType) : AreObjectsEqual(left, right, targetType); } /// /// Compare if two objects of the same type are equal. /// /// The type of objects to compare. /// The left. /// The right. /// true if the objects are equal; otherwise, false. public static Boolean AreObjectsEqual(T left, T right) where T : class => AreObjectsEqual(left, right, typeof(T)); /// /// Compare if two objects of the same type are equal. /// /// The left. /// The right. /// Type of the target. /// true if the objects are equal; otherwise, false. /// targetType. public static Boolean AreObjectsEqual(Object left, Object right, Type targetType) { if(targetType == null) { throw new ArgumentNullException(nameof(targetType)); } PropertyInfo[] properties = Runtime.PropertyTypeCache.RetrieveAllProperties(targetType).ToArray(); foreach(PropertyInfo propertyTarget in properties) { Func targetPropertyGetMethod = propertyTarget.GetCacheGetMethod(); if(propertyTarget.PropertyType.IsArray) { IEnumerable leftObj = targetPropertyGetMethod(left) as IEnumerable; IEnumerable rightObj = targetPropertyGetMethod(right) as IEnumerable; if(!AreEnumerationsEquals(leftObj, rightObj)) { return false; } } else { if(!Equals(targetPropertyGetMethod(left), targetPropertyGetMethod(right))) { return false; } } } return true; } /// /// Compare if two structures of the same type are equal. /// /// The type of structs to compare. /// The left. /// The right. /// true if the structs are equal; otherwise, false. public static Boolean AreStructsEqual(T left, T right) where T : struct => AreStructsEqual(left, right, typeof(T)); /// /// Compare if two structures of the same type are equal. /// /// The left. /// The right. /// Type of the target. /// /// true if the structs are equal; otherwise, false. /// /// targetType. public static Boolean AreStructsEqual(Object left, Object right, Type targetType) { if(targetType == null) { throw new ArgumentNullException(nameof(targetType)); } IEnumerable fields = new List(Runtime.FieldTypeCache.RetrieveAllFields(targetType)) .Union(Runtime.PropertyTypeCache.RetrieveAllProperties(targetType)); foreach(MemberInfo targetMember in fields) { switch(targetMember) { case FieldInfo field: if(Equals(field.GetValue(left), field.GetValue(right)) == false) { return false; } break; case PropertyInfo property: Func targetPropertyGetMethod = property.GetCacheGetMethod(); if(targetPropertyGetMethod != null && !Equals(targetPropertyGetMethod(left), targetPropertyGetMethod(right))) { return false; } break; } } return true; } /// /// Compare if two enumerables are equal. /// /// The type of enums to compare. /// The left. /// The right. /// /// True if two specified types are equal; otherwise, false. /// /// /// left /// or /// right. /// public static Boolean AreEnumerationsEquals(T left, T right) where T : IEnumerable { if(Equals(left, default(T))) { throw new ArgumentNullException(nameof(left)); } if(Equals(right, default(T))) { throw new ArgumentNullException(nameof(right)); } Object[] leftEnumerable = left.Cast().ToArray(); Object[] rightEnumerable = right.Cast().ToArray(); if(leftEnumerable.Length != rightEnumerable.Length) { return false; } for(Int32 i = 0; i < leftEnumerable.Length; i++) { Object leftEl = leftEnumerable[i]; Object rightEl = rightEnumerable[i]; if(!AreEqual(leftEl, rightEl, leftEl.GetType())) { return false; } } return true; } } }