前言
在现实中的编程生活里,我们时常遇到一个棘手的问题:如何比较两个相同类型的对象是否 "相等",比如在 ERP 系统中,企业的信息非常重要,每一次更新维护,都需要系统自动地详细记录更新前后企业不一致的信息、更新时间和更新人等等。
但是,直接比较通常只能告诉我们它们是否指向同一个内存地址,而不能告诉我们它们的内容是否一致,所以即使两个相同类型的对象的值都一致,程序还是偏偏说:"对不起,他们没关系!" 那我们应该怎么办呢?别急,接下来我们一起探索。
在 C# 中,要比较两个对象实例是否相等,有一些常用的方法。
比如实现 IEquatable<T>
接口、重写 Object.Equals
方法,或使用自定义比较逻辑等等。
以下是 7 种常用的方法:
1. 实现 IEquatable接口
实现 IEquatable<T>
接口是一个好习惯,就像在你的小屋里挂上一个 "欢迎光临" 的牌子,让外界知道你准备好接受比较了,实现这个接口之后,你还可以创建更强类型的比较方法。
public class Test : IEquatable<Test>
{
public int Id { get; set; }
public string Name { get; set; }
public bool Equals(Test other)
{
if (other == null)
return false;
return this.Id == other.Id && this.Name == other.Name;
}
public override bool Equals(object obj)
{
return Equals(obj as Test);
}
public override int GetHashCode()
{
return (Id, Name).GetHashCode(); // 可以使用 C# 7.3 中的元组
}
}
2. 重写 Object.Equals 和 GetHashCode 方法
这是老派的做法,但依然有效。
当你重写了 Equals
和 GetHashCode
方法后,你就能告诉对象们: "嘿,你们俩是一样的!" 。
如果你使用的是 .NET8 或更高版本时,你还可以使用 源生成器 (source generators)
特性帮助自动生成 Equals
, GetHashCode
等常用方法,编程工作更轻松!
public class Test
{
public int Id { get; set; }
public string Name { get; set; }
public override bool Equals(object obj)
{
if (obj is Test other)
{
return this.Id == other.Id && this.Name == other.Name;
}
return false;
}
public override int GetHashCode()
{
return (Id, Name).GetHashCode();
}
}
3. 重载 == 操作符
有时候,我们更倾向于使用 ==
操作符来比较对象,为了让你的代码看起来更加自然,不妨重载一下这个操作符吧!
注意,重载 ==
操作符时,通常也要重载 !=
操作符
public class Test
{
public int Id { get; set; }
public string Name { get; set; }
public static bool operator ==(Test left, Test right)
{
if (left is null && right is null) return true;
if (left is null || right is null) return false;
return left.Equals(right);
}
public static bool operator !=(Test left, Test right)
{
return !(left == right);
}
public override bool Equals(object obj)
{
return obj is Test other && Equals(other);
}
public override int GetHashCode()
{
return (Id, Name).GetHashCode();
}
}
4. 利用匿名函数或 Lambda 表达式
如果你只是偶尔需要比较两个对象,Lambda 表达式是个不错的选择,简单又直接,快速搞定一切:
Test A = new Test { Id = 1, Name = "Test" };
Test B = new Test { Id = 1, Name = "Test" };
bool areEqual = A.Id == B.Id && A.Name == B.Name; // 手动比较属性
5. 序列化为 Json 字符串再比较
如果你需要快速比较两个复杂对象,可以考虑把它们序列化成 JSON 字符串然后进行比较,这种方法虽然不是直接的对象比较方法,但简单粗暴,在某些情况下能大显身手。
序列化推荐使用 System.Text.Json
(.NET 6 及之后的版本)或 Newtonsoft.Json
(第三方库)这两个库。
using System.Text.Json;
var jsonA = JsonSerializer.Serialize(A);
var jsonB = JsonSerializer.Serialize(B);
bool areEqual = jsonA == jsonB; // 比较 JSON 字符串
需要注意的是,这种方法对于复杂类型或含有大量嵌套对象的情况,性能和效率可能比较差。
6. 直接比较
虽然说大多数情况,两个相同类型的对象之间不能直接比较,但 record
类型是个例外。
如果你使用的是 .NET 6 及之后的版本,并且对象的类型是 record
类型,那么恭喜你!
因为记录类型默认实现了 Equals
和 GetHashCode
方法,并且提供了 ==
和 !=
运算符重载,使得比较变得非常简单
public record Test(int Id,string Name);
// 创建两个 Test 对象
Test a = new Test(1, "test");
Test b = new Test(1, "test");
// 比较两个对象是否相等
bool areEqual = a == b; // 返回 true
7. 使用第三方库
最后,当然不能错过各种强大的第三方库,它们就像是你编程生活中的 "金牌助手" ,如果你懒得自己动手造轮子,它们会是你的最佳选择,推荐几个我常用的库:
EqualityComparer,一个泛型类,可以用于比较两个对象的相等性,例如:
bool areEqual = EqualityComparer<Test>.Default.Equals(A, B);
Objects Comparer,它允许逐个成员递归地比较对象,并为某些属性、字段或类型定义自定义比较逻辑,例如:
var comparer = new ObjectsComparer<Test>;
var test1 = new Test(1, "test");
var test2 = new Test(1, "test");
var isEqual = comparer.Compare(test1, test2); // 比较两个对象
......
Compare.NET Objects,可以更详细地获取两个对象之间的差异,并记录具体的差别,例如:
var test1 = new Test(1, "test");
var test2 = new Test(1, "test");
var propertyCount = 2;
CompareLogic compareLogic = new CompareLogic()
{
Config = new ComparisonConfig()
{
MaxDifferences = propertyCount //MaxDifferences的默认值是1
}
};
bool result = compareLogic.Compare(test1, test2).AreEqual;
Console.Write(result);
以上只是一些简单的例子,小伙伴们可以到这些第三方库的官网解锁更多使用姿势!
总结
比较对象是编程中的一项基本技能,它帮助我们维护数据的和谐,提升业务逻辑的效率,确保应用程序的正常运行,希望这 7 个方法能够让你在面对比较对象的任务时游刃有余!
记得在比较之前处理好 null
的情况,以免出现空引用异常哦!祝你编程愉快!
该文章在 2024/12/11 9:25:08 编辑过