C# 如何實(shí)現(xiàn)一個(gè)基于值相等性比較的字典
Intro
今天在項(xiàng)目里遇到一個(gè)需求,大概是這樣的我要比較兩個(gè) JSON 字符串是不是相等,JSON 字符串其實(shí)是一個(gè) Dictionary<string, string>
但是順序可能不同,和上一篇 record 使用場(chǎng)景 中的第一個(gè)需求類似,前面我們介紹過使用 record 可以比較方便的解決,但是我們的項(xiàng)目是 .netcoreapp3.1
的,不能使用 record
,如何比較方便的比較呢?我們能否自己實(shí)現(xiàn)一個(gè)類似于 record
的類型,基于值去比較呢?于是就有了本文的探索
StringValueDictioanry
實(shí)現(xiàn)了一個(gè)基于值進(jìn)行比較的字典,實(shí)現(xiàn)代碼如下,實(shí)現(xiàn)的比較簡(jiǎn)單,涉及到一些簡(jiǎn)單的知識(shí)點(diǎn),平時(shí)不怎么用已經(jīng)忘了怎么寫了,通過寫下面的代碼又學(xué)習(xí)了一下
先來看測(cè)試代碼吧,測(cè)試代碼如下:
[Fact] public void EqualsTest() { var abc = new { Id = 1, Name = "Tom" }; var dic1 = StringValueDictionary.FromObject(abc); var dic2 = StringValueDictionary.FromObject(new Dictionary<string, object>() { {"Name", "Tom" }, {"Id", 1}, }); Assert.True(dic1 == dic2); Assert.Equal(dic1, dic2); } [Fact] public void DistinctTest() { var abc = new { Id = 1, Name = "Tom" }; var dic1 = StringValueDictionary.FromObject(abc); var dic2 = StringValueDictionary.FromObject(new Dictionary<string, object>() { {"Id", 1}, {"Name", "Tom" }, }); var set = new HashSet<StringValueDictionary>(); set.Add(dic1); set.Add(dic2); Assert.Single(set); } [Fact] public void CloneTest() { var dic1 = StringValueDictionary.FromObject(new Dictionary<string, object>() { {"Id", 1}, {"Name", "Tom" } }); var dic2 = dic1.Clone(); Assert.False(ReferenceEquals(dic1, dic2)); Assert.True(dic1 == dic2); } [Fact] public void ImplicitConvertTest() { var abc = new { Id = 1, Name = "Tom" }; var stringValueDictionary = StringValueDictionary.FromObject(abc); Dictionary<string, string> dictionary = stringValueDictionary; Assert.Equal(stringValueDictionary.Count, dictionary.Count); var dic2 = StringValueDictionary.FromObject(dictionary); Assert.Equal(dic2, stringValueDictionary); Assert.True(dic2 == stringValueDictionary); }
從上面的代碼可能大概能看出一些實(shí)現(xiàn),重寫了默認(rèn)的 Equals
和 GetHashCode
,并重載了“==” 運(yùn)算符,并且實(shí)現(xiàn)了一個(gè)從 StringValueDictionary
到 Dictionary
的隱式轉(zhuǎn)換,來看下面的實(shí)現(xiàn)代碼:
public sealed class StringValueDictionary : IEquatable<StringValueDictionary> { private readonly Dictionary<string, string?> _dictionary = new(); private StringValueDictionary(IDictionary<string, string?> dictionary) { foreach (var pair in dictionary) { _dictionary[pair.Key] = pair.Value; } } private StringValueDictionary(StringValueDictionary dictionary) { foreach (var key in dictionary.Keys) { _dictionary[key] = dictionary[key]; } } public static StringValueDictionary FromObject(object obj) { if (obj is null) throw new ArgumentNullException(nameof(obj)); if (obj is IDictionary<string, string?> dictionary) { return new StringValueDictionary(dictionary); } if (obj is IDictionary<string, object?> dictionary2) { return new StringValueDictionary(dictionary2.ToDictionary(p => p.Key, p => p.Value?.ToString())); } if (obj is StringValueDictionary dictionary3) { return new StringValueDictionary(dictionary3); } return new StringValueDictionary(obj.GetType().GetProperties() .ToDictionary(p => p.Name, p => p.GetValue(obj)?.ToString())); } public static StringValueDictionary FromJson(string json) { Guard.NotNull(json, nameof(json)); var dic = json.JsonToObject<Dictionary<string, object?>>() .ToDictionary(x => x.Key, x => x.Value?.ToString()); return new StringValueDictionary(dic); } public StringValueDictionary Clone() => new(this); public int Count => _dictionary.Count; public bool ContainsKey(string key) => _dictionary.ContainsKey(key) ? _dictionary.ContainsKey(key) : throw new ArgumentOutOfRangeException(nameof(key)); public string? this[string key] => _dictionary[key]; public Dictionary<string, string>.KeyCollection Keys => _dictionary.Keys!; public bool Equals(StringValueDictionary? other) { if (other is null) return false; if (other.Count != Count) return false; foreach (var key in _dictionary.Keys) { if (!other.ContainsKey(key)) { return false; } if (_dictionary[key] != other[key]) { return false; } } return true; } public override bool Equals(object obj) { return Equals(obj as StringValueDictionary); } public override int GetHashCode() { var stringBuilder = new StringBuilder(); foreach (var pair in _dictionary) { stringBuilder.Append($"{pair.Key}={pair.Value}_"); } return stringBuilder.ToString().GetHashCode(); } public static bool operator ==(StringValueDictionary? current, StringValueDictionary? other) { return current?.Equals(other) == true; } public static bool operator !=(StringValueDictionary? current, StringValueDictionary? other) { return current?.Equals(other) != true; } public static implicit operator Dictionary<string, string?>(StringValueDictionary dictionary) { return dictionary._dictionary; } }
More
上述代碼實(shí)現(xiàn)的有點(diǎn)粗糙,可能會(huì)有一些問題,僅供參考
以上代碼基本實(shí)現(xiàn)了基于想要的值的相等性比較以及 Clone(復(fù)制、克?。┑哪繕?biāo)
實(shí)現(xiàn)相等性比較的時(shí)候,
Equals
和GetHashCode
方法也要重寫,如果沒有重寫GetHashCode
,編譯器也會(huì)給出警告,如果沒有重寫GetHashCode
在實(shí)際在HashSet
或者Dictionary
里可能會(huì)出現(xiàn)重復(fù)key
重載運(yùn)算符的時(shí)候需要一個(gè)靜態(tài)方法,"==" 和 "!=" 是一對(duì)操作運(yùn)算符,如果要實(shí)現(xiàn)兩個(gè)都要實(shí)現(xiàn),不能只實(shí)現(xiàn)其中一個(gè)
implicit 也算是一個(gè)特殊的運(yùn)算符,巧妙的使用隱式轉(zhuǎn)換可以大大簡(jiǎn)化代碼的寫法,
StackExchange.Redis
中就使用了implicit
來實(shí)現(xiàn)RedisValue
和string
等其他常用類型的隱式轉(zhuǎn)換
References
https://github.com/WeihanLi/WeihanLi.Common/blob/dev/src/WeihanLi.Common/Models/StringValueDictionary.cs
https://github.com/WeihanLi/WeihanLi.Common/blob/dev/test/WeihanLi.Common.Test/ModelsTest/StringValueDictionaryTest.cs
以上就是C# 如何實(shí)現(xiàn)一個(gè)基于值相等性比較的字典的詳細(xì)內(nèi)容,更多關(guān)于c# 實(shí)現(xiàn)值相等性比較的字典的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#中的文件路徑獲取函數(shù)和文件名字獲取函數(shù)小結(jié)
這篇文章主要介紹了C#中的文件路徑獲取函數(shù)和文件名字獲取函數(shù)小結(jié),本文講解了獲取絕對(duì)文件路徑、獲取文件名字、獲得包含 path 目錄信等內(nèi)容,需要的朋友可以參考下2015-01-01簡(jiǎn)單掌握Windows中C#啟動(dòng)外部程序進(jìn)程的方法
這篇文章主要介紹了Windows中C#啟動(dòng)外部程序進(jìn)程的方法,例子中同時(shí)包括了進(jìn)程關(guān)閉的方法,需要的朋友可以參考下2016-03-03c# Bitmap轉(zhuǎn)bitmapImage高效方法
本文主要介紹了c# Bitmap轉(zhuǎn)bitmapImage高效方法,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11C# 使用動(dòng)態(tài)庫(kù)DllImport("kernel32")讀寫ini文件的步驟
kernel32.dll是Windows中非常重要的32位動(dòng)態(tài)鏈接庫(kù)文件,屬于內(nèi)核級(jí)文件,這篇文章主要介紹了C# 利用動(dòng)態(tài)庫(kù)DllImport("kernel32")讀寫ini文件,需要的朋友可以參考下2023-05-05