Intro
今天在项目里遇到一个需求,大概是这样的我要比较两个 JSON 字符串是不是相等,JSON 字符串其实是一个 Dictionary<string, string>
但是顺序可能不同,和上一篇 record 使用场景 中的第一个需求类似,前面我们介绍过使用 record 可以比较方便的解决,但是我们的项目是 .netcoreapp3.1
的,不能使用 record
,如何比较方便的比较呢?我们能否自己实现一个类似于 record
的类型,基于值去比较呢?于是就有了本文的探索
StringValueDictioanry
实现了一个基于值进行比较的字典,实现代码如下,实现的比较简单,涉及到一些简单的知识点,平时不怎么用已经忘了怎么写了,通过写下面的代码又学习了一下
先来看测试代码吧,测试代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
[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); } |
从上面的代码可能大概能看出一些实现,重写了默认的 Equals
和 GetHashCode
,并重载了“==” 运算符,并且实现了一个从 StringValueDictionary
到 Dictionary
的隐式转换,来看下面的实现代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
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
上述代码实现的有点粗糙,可能会有一些问题,仅供参考
以上代码基本实现了基于想要的值的相等性比较以及 Clone(复制、克隆)的目标
实现相等性比较的时候,
Equals
和GetHashCode
方法也要重写,如果没有重写GetHashCode
,编译器也会给出警告,如果没有重写GetHashCode
在实际在HashSet
或者Dictionary
里可能会出现重复key
重载运算符的时候需要一个静态方法,"==" 和 "!=" 是一对操作运算符,如果要实现两个都要实现,不能只实现其中一个
implicit 也算是一个特殊的运算符,巧妙的使用隐式转换可以大大简化代码的写法,
StackExchange.Redis
中就使用了implicit
来实现RedisValue
和string
等其他常用类型的隐式转换
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# 如何实现一个基于值相等性比较的字典的详细内容,更多关于c# 实现值相等性比较的字典的资料请关注服务器之家其它相关文章!
原文链接:https://www.cnblogs.com/weihanli/p/14352741.html