.NET中如何深度判断2个对象相等
背景
最近在群里,有人问如何深度比较2个对象相等,感觉很有意思,就自己研究了一下,并写了一个开源的小类库,地址如下https://github.com/lamondlu/ObjectEquality。
如果想直接使用这个类库,可以使用Nuget进行安装
Install-Package ObjectEquality
对象比较有几种情况
- 对象是值类型或者String,这里仅需要判断值是否相等
- 对象是Struct,需要判断Struct的每个字段是否一致
- 对象是集合,需要判断对应位置的对象是否相等
- 对象是数组,需要判断对应位置的对象是否相等
- 对象是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
.NET中如何深度判断2个对象相等的更多相关文章
- python中的is判断引用的对象是否一致,==判断值是否相等
python中的is判断引用的对象是否一致,==判断值是否相等 a = 10 b = 20 list = [1,2,3,4,5] print(a in list) print(b not in lis ...
- JS深度判断两个对象字段相同
代码: /** * 判断此对象是否是Object类型 * @param {Object} obj */ function isObject(obj){ return Object.prototype. ...
- JS深度判断两个数组对象字段相同
/** * 判断此对象是否是Object类型 * @param {Object} obj */ function isObject(obj){ return Object.prototype.toSt ...
- 在ios开发中nil和NUll和Nilde区别————和如何判断连个对象的关系和UISlider不能拖动的问题
nil表示一个对象指针为空,针对对象 >示例代码: NSString *someString = nil; NSURL *someURL = nil; id someObject = nil; ...
- Map.containsKey方法——判断Map集合对象中是否包含指定的键名
该方法判断Map集合对象中是否包含指定的键名.如果Map集合中包含指定的键名,则返回true,否则返回false. public static void main(String[] args) { M ...
- JS中如何判断对象是对象还是数组
JS中如何判断对象是对象还是数组 一.总结 一句话总结:typeof Array.isArray === "function",Array.isArray(value)和Objec ...
- python中判断两个对象是否相等
#coding=utf-8#比较两个对象是否相等#python 2中使用cmp(),==,is#is 主要是判断 2 个变量是否引用的是同一个对象,如果是的话,则返回 true,否则返回 false. ...
- Java中如何判断两个对象是否相等(Java equals and ==)
原文https://www.dutycode.com/post-140.html 如何判断两个对象相等,这个问题实际上可以看做是如何对equals方法和hashcode方法的理解. 从以下几个点来理解 ...
- js判断对象中是否存在某一项和判断是否是对象
1.判断是否为对象 let str = { name: '第一', age: 12 } console.log(typeof str== "object") 2.判断对象中是否有某 ...
随机推荐
- ELK logstash geoip值为空故障排查
首先我们用的是elasticsearch+kibana+logstash+filebeat 客户端filebeat收集日志后经过服务端logstash规则处理后储存到elasticsearch中,在k ...
- Analisis of Hello2 source
GreetingServlet.java @WebServlet("/greeting") //为servelt指定URL pattern为:/greeting public cl ...
- time-based基于google key生成6位验证码(google authenticator)
由于公司服务器启用了双因子认证,登录时需要再次输入谷歌身份验证器生成的验证码.而生成验证码是基于固定的算法的,以当前时间为基础,基于每个人的google key去生成一个6位的验证码.也就是说,只要是 ...
- 2018-2019-2 网络对抗技术 20162329 Exp1 PC平台逆向破解
目录 1.实践目标 2.实验内容 2.1 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数. 2.2 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getS ...
- 编程菜鸟的日记-初学尝试编程-C++ Primer Plus 第6章编程练习8
#include <iostream> #include <fstream> #include <cstdlib> const int SIZE=20; using ...
- mysql数据库内容相关操作
第一:介绍 mysql数据内容的操作主要是: INSERT实现数据的插入 UPDATE实现数据的更新 DLETE实现数据的删除 SELECT实现数据的查询. 第二:增(insert) 1.插入完整的数 ...
- Flask的上下文源码剖析
先写一段Flask程序 from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return ...
- Java课堂笔记(一):Java基础
本篇博客将对Java中的数据类型.操作符,常量与变量和数组进行介绍.这些内容都是Java中最基本的知识,也是初学Java时最开始就需要了解的东西. Java数据类型 Java是一种强类型的语言,这就意 ...
- Spring整合MybatisPlus学习笔记
简介 MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发.提高效率而生 特性 无侵入:只做增强不做改变,引入它不会对现有 ...
- spring MVC 的MultipartFile转File读取
转自:http://www.cnblogs.com/hahaxiaoyu/p/5102900.html 第一种方法: MultipartFile file = xxx; Commo ...