.NET中如何深度判断2个对象相等

背景

最近在群里,有人问如何深度比较2个对象相等,感觉很有意思,就自己研究了一下,并写了一个开源的小类库,地址如下https://github.com/lamondlu/ObjectEquality。

如果想直接使用这个类库,可以使用Nuget进行安装

Install-Package ObjectEquality

对象比较有几种情况

  1. 对象是值类型或者String,这里仅需要判断值是否相等
  2. 对象是Struct,需要判断Struct的每个字段是否一致
  3. 对象是集合,需要判断对应位置的对象是否相等
  4. 对象是数组,需要判断对应位置的对象是否相等
  5. 对象是Class, 需要判断Class的每个字段是否一致

这里可能有缺漏,大家可以帮我补充。

编写代码

这里我首先创建了一个IEquality接口,在其中定义了一个IsEqual方法,这个方法就是判断2个对象是否一致的方法。后面我会针对上面说明的几种对比场景,分别创建对应的实现类。

    public interface IEquality
    {
        Func<object, bool> MatchCondition { get; }
        bool IsEqual(object source, object target);
    }

这里MatchCondition是一个委托,它定义了当前对比类的匹配条件。

第二步,我们针对上述的几种对比场景,创建对应的实现类

值类型相等判断实现类

    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);
        }
    }

值类型的判断比较简单,直接调用Object类的Equals方法即可。

String类型虽然不是值类型,但是这里我们需要把它归到值类型中。

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对象不相等。

这里EqualityCollection是判断器集合,后续会添加这个类的代码。

集合相等判断实现类

    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()
        };
    }

这里我们定义了一个静态类,来存储程序中使用的所有判断器。

这里在判断器集合中,实现类的其实是有顺序的,StructEquality必须要放到ValueTypeEquality的前面,因为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);
        }
    }

前面所有实现类的访问级别都是Internal, 所以我们需要创建一个判断器入口类, 外部只能通过ObjectEquality类的实例来实现判断相等。

最终效果

下面我列举几个测试用例,看看效果是不是我们想要的

对比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

时间: 2024-10-01 02:40:03

.NET中如何深度判断2个对象相等的相关文章

在ios开发中nil和NUll和Nilde区别————和如何判断连个对象的关系

nil表示一个对象指针为空,针对对象 >示例代码: NSString *someString = nil; NSURL *someURL = nil; id someObject = nil; if (anotherObject == nil) // do something Nil表示一个类指针为空,针对类 >示例代码: Class someClass = Nil; Class anotherClass = [NSString class]; NULL表示基本数据类型为空,基本类型 >

Map.containsKey方法——判断Map集合对象中是否包含指定的键名

该方法判断Map集合对象中是否包含指定的键名.如果Map集合中包含指定的键名,则返回true,否则返回false. public static void main(String[] args) { Map map = new HashMap(); //定义Map对象 map.put("apple", "新鲜的苹果"); //向集合中添加对象 map.put("computer", "配置优良的计算机"); map.put(&q

Java中如何判断两个对象是否相等(Java equals and ==)

原文https://www.dutycode.com/post-140.html 如何判断两个对象相等,这个问题实际上可以看做是如何对equals方法和hashcode方法的理解. 从以下几个点来理解equals和hashCode方法: 1.equals的作用及与==的区别. 2.hashcode的作用及与equals的关系. 1.equals的作用及与==的区别. equals被用来判断两个对象是否相等. equals通常用来比较两个对象的内容是否相等,==用来比较两个对象的地址是否相等. e

如何判断两个对象相等

前言 虽然标题写的是如何判断两个对象相等,但本篇我们不仅仅判断两个对象相等,实际上,我们要做到的是如何判断两个参数相等,而这必然会涉及到多种类型的判断. 相等 什么是相等?在<JavaScript专题之去重>中,我们认为只要 === 的结果为 true,两者就相等,然而今天我们重新定义相等: 我们认为: NaN 和 NaN 是相等 [1] 和 [1] 是相等 {value: 1} 和 {value: 1} 是相等 不仅仅是这些长得一样的,还有 1 和 new Number(1) 是相等 'Cu

OpenGL和D3D11中的深度模版测试

    在OpenGL和D3D11的管线中,像素shader之后的操作就是深度模版测试,深度模版测试是以sample为单位进行的,就是一个像素上可以有多个采样点,每个采样点都有深度信息.深度模版测试对每个采样点都要进行一次,如果是msaa,最后要对每次采样的像素结果进行resolve,得到最终的结果.在下面的链接中有msaa的介绍. http://www.cnblogs.com/mikewolf2002/archive/2012/11/22/2783235.html     深度模版测试的流程如

图解js中常用的判断浏览器窗体、用户屏幕可视区域大小位置的方法

有时我们需要获得浏览器窗口或屏幕的大小.窗口下拉框下拉的距离等数据,对应这些需求,js中提供了不少解决方法,只是数量稍多容易混淆它们各自的意义,下面咱们用图例来解释下12个常见对象属性的作用. 其中有6个常用的浏览器窗体属性(由于offsetWidth/Height在不同浏览器下表现有出入,故不在本章讨论): document.documentElement.clientWidth document.documentElement.clientHeight document.documentEl

如何判断一个C++对象是否在堆上(通过GetProcessHeaps取得所有堆,然后与对象地址比较即可),附许多精彩评论

在帖子如何判断一个C++对象是否在堆栈上 中, 又有人提出如何判断一个C++对象是否在堆上. 其实我们可以参照那个帖子的方法类似实现,我们知道堆就是Heap,在windows上我们可以通过GetProcessHeaps来得到所有的堆句柄,而我们这里只要知道Windows上的Heap Handle,其实就是堆的起始地址,就可以写如下代码了. #include <iostream>#include <windows.h> using namespace std; BOOL IsObje

图像识别中的深度学习 转

转:http://mp.weixin.qq.com/s?__biz=MzAwNDExMTQwNQ==&mid=209152042&idx=1&sn=fa0053e66cad3d2f7b107479014d4478#rd#opennewwindow 1.深度学习发展历史 深度学习是近十年来人工智能领域取得的重要突破.它在语音识别.自然语言处理.计算机视觉.图像与视频分析.多媒体等诸多领域的应用取得了巨大成功.现有的深度学习模型属于神经网络.神经网络的起源可追溯到20世纪40年代,曾经

windows中根据进程PID查找进程对象过程深入分析

这里windows和Linxu系列的PID 管理方式有所不同,windows中进程的PID和句柄没有本质区别,根据句柄索引对象和根据PID或者TID查找进程或者线程的步骤也是一样的. 句柄是针对进程而言,也就是句柄一定所属于某个进程,为某个进程私有的资源.句柄的分配来自于进程私有的句柄表.而进程PID和线程TID和句柄的分配原理是一样的,但是来源就不同了.进程PID来源于系统中一张全局的句柄表PSpcidtable.从级别上来说Pspcidtable和进程对象eprcess中的objecttab