最近在羣裏,有人問如何深度比較2個對象相等,感受頗有意思,就本身研究了一下,並寫了一個開源的小類庫,地址以下https://github.com/lamondlu/ObjectEquality。html
若是想直接使用這個類庫,可使用Nuget進行安裝git
Install-Package ObjectEquality
這裏可能有缺漏,你們能夠幫我補充。github
這裏我首先建立了一個<code>IEquality</code>接口,在其中定義了一個<code>IsEqual</code>方法,這個方法就是判斷2個對象是否一致的方法。後面我會針對上面說明的幾種對比場景,分別建立對應的實現類。c#
public interface IEquality { Func<object, bool> MatchCondition { get; } bool IsEqual(object source, object target); }
這裏<code>MatchCondition</code>是一個委託,它定義了當前對比類的匹配條件。數組
第二步,咱們針對上述的幾種對比場景,建立對應的實現類測試
internal class ValueTypeEquality : IEquality { public Func<object, bool> MatchCondition { get { return p => p.GetType().IsValueType || p.GetType() == typeof(string); } } public bool IsEqual(object source, object target) { return source.Equals(target); } }
值類型的判斷比較簡單,直接調用<code>Object</code>類的<code>Equals</code>方法便可。spa
String類型雖然不是值類型,可是這裏咱們須要把它歸到值類型中。code
internal class StructEquality : IEquality { public Func<object, bool> MatchCondition { get { return p => p.GetType().IsValueType && !p.GetType().IsPrimitive && !p.GetType().IsEnum; } } public bool IsEqual(object source, object target) { var type = source.GetType(); foreach (var prop in type.GetProperties()) { var equality = EqualityCollection.Equalities .First(p => p.MatchCondition(prop.GetValue(source))); var result = equality.IsEqual(prop.GetValue(source), prop.GetValue(target)); if (!result) { return false; } } return true; } }
這裏咱們讀取了Struct中的每一個屬性,分別進行判斷,若是有一個判斷失敗,即認爲2個Struct對象不相等。htm
這裏<code>EqualityCollection</code>是判斷器集合,後續會添加這個類的代碼。對象
internal class GenericCollectionEquality : IEquality { public Func<object, bool> MatchCondition { get { return p => p.GetType().IsGenericType; } } public bool IsEqual(object source, object target) { var type = source.GetType(); var genericType = type.GetGenericArguments()[0]; var genericCollectionType = typeof(IEnumerable<>).MakeGenericType(genericType); if (type.GetInterfaces().Any(p => p == genericCollectionType)) { var countMethod = type.GetMethod("get_Count"); var sourceCount = (int)countMethod.Invoke(source, null); var targetCount = (int)countMethod.Invoke(target, null); if (sourceCount != targetCount) { return false; } var sourceCollection = (source as IEnumerable<object>).ToList(); var targetCollection = (target as IEnumerable<object>).ToList(); for (var i = 0; i < sourceCount; i++) { var equality = EqualityCollection.Equalities.First(p => p.MatchCondition(sourceCollection[i])); var result = equality.IsEqual(sourceCollection[i], targetCollection[i]); if (!result) { return false; } } } return true; } }
這裏咱們首先判斷了集合的元素的數量是否一致,若是不一致,即這2個集合不相等。若是一致,咱們繼續判斷對應位置的每一個元素是否一致,若是所有都一直,則2個集合至關,不然2個集合不相等。
internal class ArrayEquality : IEquality { public Func<object, bool> MatchCondition { get { return p => p.GetType().IsArray; } } public bool IsEqual(object source, object target) { Array s = source as Array; Array t = target as Array; if (s.Length != t.Length) { return false; } for (var i = 0; i < s.Length; i++) { var equality = EqualityCollection.Equalities .First(p => p.MatchCondition(s.GetValue(i))); var result = equality.IsEqual(s.GetValue(i), t.GetValue(i)); if (!result) { return false; } } return true; } }
數組相等的判斷相似集合,咱們首先判斷數組的長度是否一致,而後判斷對應位置的元素是否一致。
internal class ClassEquality : IEquality { public Func<object, bool> MatchCondition { get { return p => p.GetType().IsClass; } } public bool IsEqual(object source, object target) { var type = source.GetType(); foreach (var prop in type.GetProperties()) { var equality = EqualityCollection.Equalities .First(p => p.MatchCondition(prop.GetValue(source))); var result = equality.IsEqual(prop.GetValue(source), prop.GetValue(target)); if (!result) { return false; } } return true; } }
public static class EqualityCollection { public static readonly List<IEquality> Equalities = new List<IEquality> { new StructEquality(), new ValueTypeEquality(), new ArrayEquality(), new GenericCollectionEquality(), new ClassEquality() }; }
這裏咱們定義了一個靜態類,來存儲程序中使用的全部判斷器。
這裏在判斷器集合中,實現類的實際上是有順序的,<code>StructEquality</code>必需要放到<code>ValueTypeEquality</code>的前面,由於Struct也是值類型,若是不放到最前面,會致使判斷失敗。
public class ObjectEquality { public bool IsEqual(object source, object target) { if (source.GetType() != target.GetType()) { return false; } if (source == null && target == null) { return true; } else if (source == null && target != null) { return false; } else if (source != null && target == null) { return false; } var equality = EqualityCollection.Equalities .First(p => p.MatchCondition(source)); return equality.IsEqual(source, target); } }
前面全部實現類的訪問級別都是<code>Internal</code>, 因此咱們須要建立一個判斷器入口類, 外部只能經過<code>ObjectEquality</code>類的實例來實現判斷相等。
下面我列舉幾個測試用例,看看效果是否是咱們想要的
public struct DemoStruct { public int Id { get; set; } public string Name { get; set; } }
var a = new DemoStruct(); a.Id = 1; a.Name = "Test"; var b = new DemoStruct(); b.Id = 1; b.Name = "Test"; var c = new DemoStruct(); b.Id = 2; b.Name = "Test"; ObjectEquality objectEquality = new ObjectEquality(); objectEquality.IsEqual(a,b); //true objectEquality.IsEqual(a,c); //false
public class SimpleClass { public int Id { get; set; } public string Name { get; set; } }
var a = new SimpleClass { Id = 1, Name = "A" }; var b = new SimpleClass { Id = 1, Name = "A" }; var c = new SimpleClass { Id = 2, Name = "A" }; ObjectEquality objectEquality = new ObjectEquality(); objectEquality.IsEqual(a,b); //true objectEquality.IsEqual(a,c); //false
var a = new int[] { 1, 2, 3 }; var b = new int[] { 1, 2, 3 }; var c = new int[] { 1, 1, 2 }; ObjectEquality objectEquality = new ObjectEquality(); objectEquality.IsEqual(a,b); //true objectEquality.IsEqual(a,c); //false
原文出處:https://www.cnblogs.com/lwqlun/p/10159770.html