C# 如何實(shí)現(xiàn)一個基于值相等性比較的字典
Intro
今天在項(xiàng)目里遇到一個需求,大概是這樣的我要比較兩個 JSON 字符串是不是相等,JSON 字符串其實(shí)是一個 Dictionary<string, string> 但是順序可能不同,和上一篇 record 使用場景 中的第一個需求類似,前面我們介紹過使用 record 可以比較方便的解決,但是我們的項(xiàng)目是 .netcoreapp3.1 的,不能使用 record,如何比較方便的比較呢?我們能否自己實(shí)現(xiàn)一個類似于 record 的類型,基于值去比較呢?于是就有了本文的探索
StringValueDictioanry
實(shí)現(xiàn)了一個基于值進(jìn)行比較的字典,實(shí)現(xiàn)代碼如下,實(shí)現(xiàn)的比較簡單,涉及到一些簡單的知識點(diǎn),平時不怎么用已經(jīng)忘了怎么寫了,通過寫下面的代碼又學(xué)習(xí)了一下
先來看測試代碼吧,測試代碼如下:
[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)了一個從 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)粗糙,可能會有一些問題,僅供參考
以上代碼基本實(shí)現(xiàn)了基于想要的值的相等性比較以及 Clone(復(fù)制、克?。┑哪繕?biāo)
實(shí)現(xiàn)相等性比較的時候,
Equals和GetHashCode方法也要重寫,如果沒有重寫GetHashCode,編譯器也會給出警告,如果沒有重寫GetHashCode在實(shí)際在HashSet或者Dictionary里可能會出現(xiàn)重復(fù)key重載運(yùn)算符的時候需要一個靜態(tài)方法,"==" 和 "!=" 是一對操作運(yùn)算符,如果要實(shí)現(xiàn)兩個都要實(shí)現(xiàn),不能只實(shí)現(xiàn)其中一個
implicit 也算是一個特殊的運(yùn)算符,巧妙的使用隱式轉(zhuǎ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)一個基于值相等性比較的字典的詳細(xì)內(nèi)容,更多關(guān)于c# 實(shí)現(xiàn)值相等性比較的字典的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#基礎(chǔ)教程之類class與結(jié)構(gòu)struct的區(qū)別
struct是值類型,創(chuàng)建一個struct類型的實(shí)例被分配在棧上,class是引用類型,創(chuàng)建一個class類型實(shí)例被分配在托管堆上,下面這篇文章主要給大家介紹了關(guān)于C#基礎(chǔ)教程之類class與結(jié)構(gòu)struct區(qū)別的相關(guān)資料,需要的朋友可以參考下2022-11-11
C#調(diào)用SQL?Server中有參數(shù)的存儲過程
這篇文章介紹了C#調(diào)用SQL?Server中有參數(shù)存儲過程的方法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-03-03
C# 獲取進(jìn)程退出代碼的實(shí)現(xiàn)示例
這篇文章主要介紹了C# 獲取進(jìn)程退出代碼的實(shí)現(xiàn)示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-02-02

