C# 实现一个基于值相等性比较的字典
C# 實(shí)現(xiàn)一個(gè)基于值相等性比較的字典
Intro
今天在項(xiàng)目里遇到一個(gè)需求,大概是這樣的我要比較兩個(gè) JSON 字符串是不是相等,JSON 字符串其實(shí)是一個(gè) Dictionary<string, string> 但是順序可能不同,和上一篇 record 使用場(chǎng)景中的第一個(gè)需求類(lèi)似,前面我們介紹過(guò)使用 record 可以比較方便的解決,但是我們的項(xiàng)目是 .netcoreapp3.1 的,不能使用 record,如何比較方便的比較呢?我們能否自己實(shí)現(xiàn)一個(gè)類(lèi)似于 record 的類(lèi)型,基于值去比較呢?于是就有了本文的探索
StringValueDictioanry
實(shí)現(xiàn)了一個(gè)基于值進(jìn)行比較的字典,實(shí)現(xiàn)代碼如下,實(shí)現(xiàn)的比較簡(jiǎn)單,涉及到一些簡(jiǎn)單的知識(shí)點(diǎn),平時(shí)不怎么用已經(jīng)忘了怎么寫(xiě)了,通過(guò)寫(xiě)下面的代碼又學(xué)習(xí)了一下
先來(lái)看測(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),重寫(xiě)了默認(rèn)的 Equals 和 GetHashCode,并重載了“==” 運(yùn)算符,并且實(shí)現(xiàn)了一個(gè)從 StringValueDictionary 到 Dictionary 的隱式轉(zhuǎn)換,來(lái)看下面的實(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ì)有一些問(wèn)題,僅供參考
以上代碼基本實(shí)現(xiàn)了基于想要的值的相等性比較以及 Clone(復(fù)制、克隆)的目標(biāo)
實(shí)現(xiàn)相等性比較的時(shí)候,Equals 和 GetHashCode 方法也要重寫(xiě),如果沒(méi)有重寫(xiě) GetHashCode,編譯器也會(huì)給出警告,如果沒(méi)有重寫(xiě) 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)化代碼的寫(xiě)法,StackExchange.Redis 中就使用了 implicit 來(lái)實(shí)現(xiàn) RedisValue 和 string 等其他常用類(lèi)型的隱式轉(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
總結(jié)
以上是生活随笔為你收集整理的C# 实现一个基于值相等性比较的字典的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 聊聊Interlocked.Compar
- 下一篇: 巧用lock解决缓存击穿的解决方案