背景

最近在群里,有人问如何深度比较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

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

  1. python中的is判断引用的对象是否一致,==判断值是否相等

    python中的is判断引用的对象是否一致,==判断值是否相等 a = 10 b = 20 list = [1,2,3,4,5] print(a in list) print(b not in lis ...

  2. JS深度判断两个对象字段相同

    代码: /** * 判断此对象是否是Object类型 * @param {Object} obj */ function isObject(obj){ return Object.prototype. ...

  3. JS深度判断两个数组对象字段相同

    /** * 判断此对象是否是Object类型 * @param {Object} obj */ function isObject(obj){ return Object.prototype.toSt ...

  4. 在ios开发中nil和NUll和Nilde区别————和如何判断连个对象的关系和UISlider不能拖动的问题

    nil表示一个对象指针为空,针对对象 >示例代码: NSString *someString = nil; NSURL *someURL = nil; id someObject = nil; ...

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

    该方法判断Map集合对象中是否包含指定的键名.如果Map集合中包含指定的键名,则返回true,否则返回false. public static void main(String[] args) { M ...

  6. JS中如何判断对象是对象还是数组

    JS中如何判断对象是对象还是数组 一.总结 一句话总结:typeof Array.isArray === "function",Array.isArray(value)和Objec ...

  7. python中判断两个对象是否相等

    #coding=utf-8#比较两个对象是否相等#python 2中使用cmp(),==,is#is 主要是判断 2 个变量是否引用的是同一个对象,如果是的话,则返回 true,否则返回 false. ...

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

    原文https://www.dutycode.com/post-140.html 如何判断两个对象相等,这个问题实际上可以看做是如何对equals方法和hashcode方法的理解. 从以下几个点来理解 ...

  9. js判断对象中是否存在某一项和判断是否是对象

    1.判断是否为对象 let str = { name: '第一', age: 12 } console.log(typeof str== "object") 2.判断对象中是否有某 ...

随机推荐

  1. event、fly.js、购物车特效

    先总结下区别: #鼠标相对于浏览器窗口可视区域的X,Y坐标(窗口坐标),可视区域不包括工具栏和滚动条. event.clientX.event.clientY #鼠标相对于document文档区域的x ...

  2. 2013年省赛I题 Thrall’s Dream

    2013年省赛I题判断单向联通,用bfs剪枝:从小到大跑,如果遇到之前跑过的点(也就是编号小于当前点的点),就o(n)传递关系. bfs #include<iostream> #inclu ...

  3. MSDN i TELL YOU 又更新了,win10 1809版本的 3月29日的

    MSDN i TELL YOU 又更新了,1809版本的 3月29日的 WINDOWS 10 现在只有64位的 很好,估计 64位的普及了. 是一大改变

  4. String StringBuffer StringBulider 详细看https://www.cnblogs.com/su-feng

    主要区别:运行速度和线程安全 StringBuilder > StringBuffer > String String最慢是因为字符串常量不可改变,例如 str  +“cccc”   如果 ...

  5. Do-Now—团队冲刺博客一(领航篇)

    Do Now -- 团队冲刺博客一(领航篇) 团队博客总目录:团队作业第一周 团队作业第二周 Do Now -- 团队冲刺博客一 领航目标 ① 各个成员在 Alpha 阶段认领的任务 ② 明日各个成员 ...

  6. MicroSoft CryptoAPI data/file encrypt/decrypt

    linux 用第三方库 Crypto++, 还未实战. CryptoAPI使用两种密钥:会话密钥与公共/私人密钥对.会话密钥使用相同的加密和解密密钥,这种算法较快,但必须保证密钥的安全传递.公共/私人 ...

  7. NOIP2017感悟

    Day1 第一次会做的题这么多却比以前靠的更差. 其实停课期间水平还是提升了很多,但是做题速度和心态问题一就是我最难克服的一个地方. 题目很简单,但是又很恶心,主要是我代码能力太差,第二题调不出来,第 ...

  8. c++标准---for循环新用法

    贪吃蛇游戏的c++版本,从GitHub上clone下来的,在startinterface.cpp文件中有如下代码: void   StartInterface::PrintText() { for ( ...

  9. 如何修改image文件

    方法一:mount成为一个loop device 参考http://smilejay.com/2012/08/mount-an-image-file/ 方法一:找出分区开始的开始位置,使用mount命 ...

  10. MapReduce计算模型的优化

    MapReduce 计算模型的优化涉及了方方面面的内容,但是主要集中在两个方面:一是计算性能方面的优化:二是I/O操作方面的优化.这其中,又包含六个方面的内容. 1.任务调度 任务调度是Hadoop中 ...