RaspberryIO_26/Swan.Lite/ObjectComparer.cs

180 lines
6.7 KiB
C#
Raw Normal View History

2019-12-04 18:57:18 +01:00
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Swan.Reflection;
2019-12-08 19:54:52 +01:00
namespace Swan {
/// <summary>
/// Represents a quick object comparer using the public properties of an object
/// or the public members in a structure.
/// </summary>
public static class ObjectComparer {
2019-12-04 18:57:18 +01:00
/// <summary>
2019-12-08 19:54:52 +01:00
/// Compare if two variables of the same type are equal.
2019-12-04 18:57:18 +01:00
/// </summary>
2019-12-08 19:54:52 +01:00
/// <typeparam name="T">The type of objects to compare.</typeparam>
/// <param name="left">The left.</param>
/// <param name="right">The right.</param>
/// <returns><c>true</c> if the variables are equal; otherwise, <c>false</c>.</returns>
public static Boolean AreEqual<T>(T left, T right) => AreEqual(left, right, typeof(T));
/// <summary>
/// Compare if two variables of the same type are equal.
/// </summary>
/// <param name="left">The left.</param>
/// <param name="right">The right.</param>
/// <param name="targetType">Type of the target.</param>
/// <returns>
/// <c>true</c> if the variables are equal; otherwise, <c>false</c>.
/// </returns>
/// <exception cref="ArgumentNullException">targetType.</exception>
public static Boolean AreEqual(Object left, Object right, Type targetType) {
if(targetType == null) {
throw new ArgumentNullException(nameof(targetType));
}
if(Definitions.BasicTypesInfo.Value.ContainsKey(targetType)) {
return Equals(left, right);
}
return targetType.IsValueType || targetType.IsArray ? AreStructsEqual(left, right, targetType) : AreObjectsEqual(left, right, targetType);
}
/// <summary>
/// Compare if two objects of the same type are equal.
/// </summary>
/// <typeparam name="T">The type of objects to compare.</typeparam>
/// <param name="left">The left.</param>
/// <param name="right">The right.</param>
/// <returns><c>true</c> if the objects are equal; otherwise, <c>false</c>.</returns>
public static Boolean AreObjectsEqual<T>(T left, T right) where T : class => AreObjectsEqual(left, right, typeof(T));
/// <summary>
/// Compare if two objects of the same type are equal.
/// </summary>
/// <param name="left">The left.</param>
/// <param name="right">The right.</param>
/// <param name="targetType">Type of the target.</param>
/// <returns><c>true</c> if the objects are equal; otherwise, <c>false</c>.</returns>
/// <exception cref="ArgumentNullException">targetType.</exception>
public static Boolean AreObjectsEqual(Object left, Object right, Type targetType) {
if(targetType == null) {
throw new ArgumentNullException(nameof(targetType));
}
PropertyInfo[] properties = PropertyTypeCache.DefaultCache.Value.RetrieveAllProperties(targetType).ToArray();
foreach(PropertyInfo propertyTarget in properties) {
Func<Object, Object> 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;
}
/// <summary>
/// Compare if two structures of the same type are equal.
/// </summary>
/// <typeparam name="T">The type of structs to compare.</typeparam>
/// <param name="left">The left.</param>
/// <param name="right">The right.</param>
/// <returns><c>true</c> if the structs are equal; otherwise, <c>false</c>.</returns>
public static Boolean AreStructsEqual<T>(T left, T right) where T : struct => AreStructsEqual(left, right, typeof(T));
/// <summary>
/// Compare if two structures of the same type are equal.
/// </summary>
/// <param name="left">The left.</param>
/// <param name="right">The right.</param>
/// <param name="targetType">Type of the target.</param>
/// <returns>
/// <c>true</c> if the structs are equal; otherwise, <c>false</c>.
/// </returns>
/// <exception cref="ArgumentNullException">targetType.</exception>
public static Boolean AreStructsEqual(Object left, Object right, Type targetType) {
if(targetType == null) {
throw new ArgumentNullException(nameof(targetType));
}
IEnumerable<MemberInfo> fields = new List<MemberInfo>(FieldTypeCache.DefaultCache.Value.RetrieveAllFields(targetType)).Union(PropertyTypeCache.DefaultCache.Value.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<Object, Object> targetPropertyGetMethod = property.GetCacheGetMethod();
if(targetPropertyGetMethod != null &&
!Equals(targetPropertyGetMethod(left), targetPropertyGetMethod(right))) {
return false;
}
break;
}
}
return true;
}
/// <summary>
/// Compare if two enumerables are equal.
/// </summary>
/// <typeparam name="T">The type of enums to compare.</typeparam>
/// <param name="left">The left.</param>
/// <param name="right">The right.</param>
/// <returns>
/// <c>true</c> if two specified types are equal; otherwise, <c>false</c>.
/// </returns>
/// <exception cref="ArgumentNullException">
/// left
/// or
/// right.
/// </exception>
public static Boolean AreEnumerationsEquals<T>(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<Object>().ToArray();
Object[] rightEnumerable = right.Cast<Object>().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;
}
}
2019-12-04 18:57:18 +01:00
}