.NET中如何深度判斷2個對象相等

背景

最近在羣裏,有人問如何深度比較2個對象相等,感受頗有意思,就本身研究了一下,並寫了一個開源的小類庫,地址以下https://github.com/lamondlu/ObjectEquality。html

若是想直接使用這個類庫,可使用Nuget進行安裝git

Install-Package ObjectEquality

對象比較有幾種狀況

  1. 對象是值類型或者String,這裏僅須要判斷值是否相等
  2. 對象是Struct,須要判斷Struct的每一個字段是否一致
  3. 對象是集合,須要判斷對應位置的對象是否相等
  4. 對象是數組,須要判斷對應位置的對象是否相等
  5. 對象是Class, 須要判斷Class的每一個字段是否一致

這裏可能有缺漏,你們能夠幫我補充。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

Struct相等判斷實現類

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>類的實例來實現判斷相等。

最終效果

下面我列舉幾個測試用例,看看效果是否是咱們想要的

對比Struct

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

相關文章
相關標籤/搜索