前言
在現(xiàn)實(shí)中的編程生活里,我們時常遇到一個棘手的問題:如何比較兩個相同類型的對象是否 "相等",比如在 ERP 系統(tǒng)中,企業(yè)的信息非常重要,每一次更新維護(hù),都需要系統(tǒng)自動地詳細(xì)記錄更新前后企業(yè)不一致的信息、更新時間和更新人等等。
但是,直接比較通常只能告訴我們它們是否指向同一個內(nèi)存地址,而不能告訴我們它們的內(nèi)容是否一致,所以即使兩個相同類型的對象的值都一致,程序還是偏偏說:"對不起,他們沒關(guān)系!" 那我們應(yīng)該怎么辦呢?別急,接下來我們一起探索。
在 C# 中,要比較兩個對象實(shí)例是否相等,有一些常用的方法。
比如實(shí)現(xiàn) IEquatable<T>
接口、重寫 Object.Equals
方法,或使用自定義比較邏輯等等。
以下是 7 種常用的方法:
1. 實(shí)現(xiàn) IEquatable接口
實(shí)現(xiàn) IEquatable<T>
接口是一個好習(xí)慣,就像在你的小屋里掛上一個 "歡迎光臨" 的牌子,讓外界知道你準(zhǔn)備好接受比較了,實(shí)現(xiàn)這個接口之后,你還可以創(chuàng)建更強(qiáng)類型的比較方法。
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 方法
這是老派的做法,但依然有效。
當(dāng)你重寫了 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. 利用匿名函數(shù)或 Lambda 表達(dá)式
如果你只是偶爾需要比較兩個對象,Lambda 表達(dá)式是個不錯的選擇,簡單又直接,快速搞定一切:
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 字符串再比較
如果你需要快速比較兩個復(fù)雜對象,可以考慮把它們序列化成 JSON 字符串然后進(jìn)行比較,這種方法雖然不是直接的對象比較方法,但簡單粗暴,在某些情況下能大顯身手。
序列化推薦使用 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 字符串
需要注意的是,這種方法對于復(fù)雜類型或含有大量嵌套對象的情況,性能和效率可能比較差。
6. 直接比較
雖然說大多數(shù)情況,兩個相同類型的對象之間不能直接比較,但 record
類型是個例外。
如果你使用的是 .NET 6 及之后的版本,并且對象的類型是 record
類型,那么恭喜你!
因為記錄類型默認(rèn)實(shí)現(xiàn)了 Equals
和 GetHashCode
方法,并且提供了 ==
和 !=
運(yùn)算符重載,使得比較變得非常簡單
public record Test(int Id,string Name);
// 創(chuàng)建兩個 Test 對象
Test a = new Test(1, "test");
Test b = new Test(1, "test");
// 比較兩個對象是否相等
bool areEqual = a == b; // 返回 true
7. 使用第三方庫
最后,當(dāng)然不能錯過各種強(qiáng)大的第三方庫,它們就像是你編程生活中的 "金牌助手" ,如果你懶得自己動手造輪子,它們會是你的最佳選擇,推薦幾個我常用的庫:
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,可以更詳細(xì)地獲取兩個對象之間的差異,并記錄具體的差別,例如:
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的默認(rèn)值是1
}
};
bool result = compareLogic.Compare(test1, test2).AreEqual;
Console.Write(result);
以上只是一些簡單的例子,小伙伴們可以到這些第三方庫的官網(wǎng)解鎖更多使用姿勢!
總結(jié)
比較對象是編程中的一項基本技能,它幫助我們維護(hù)數(shù)據(jù)的和諧,提升業(yè)務(wù)邏輯的效率,確保應(yīng)用程序的正常運(yùn)行,希望這 7 個方法能夠讓你在面對比較對象的任務(wù)時游刃有余!
記得在比較之前處理好 null
的情況,以免出現(xiàn)空引用異常哦!
該文章在 2024/12/13 9:26:22 編輯過