C# 实现一个基于值相等性比较的字典
C# 实现一个基于值相等性比较的字典
Intro
今天在项目里遇到一个需求,大概是这样的我要比较两个 JSON 字符串是不是相等,JSON 字符串其实是一个 Dictionary<string, string> 但是顺序可能不同,和上一篇 record 使用场景中的第一个需求类似,前面我们介绍过使用 record 可以比较方便的解决,但是我们的项目是 .netcoreapp3.1 的,不能使用 record,如何比较方便的比较呢?我们能否自己实现一个类似于 record 的类型,基于值去比较呢?于是就有了本文的探索
StringValueDictioanry
实现了一个基于值进行比较的字典,实现代码如下,实现的比较简单,涉及到一些简单的知识点,平时不怎么用已经忘了怎么写了,通过写下面的代码又学习了一下
先来看测试代码吧,测试代码如下:
[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 的隐式转换,来看下面的实现代码:
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重载运算符的时候需要一个静态方法,"==" 和 "!=" 是一对操作运算符,如果要实现两个都要实现,不能只实现其中一个
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# 实现一个基于值相等性比较的字典的更多相关文章
- 一个基于mysql构建的队列表
通常大家都会使用redis作为应用的任务队列表,redis的List结构,在一段进行任务的插入,在另一端进行任务的提取. 任务的插入 $redis->lPush("key:task:l ...
- 关于实现一个基于文件持久化的EventStore的核心构思
大家知道enode框架的架构是基于ddd+event sourcing的思想.我们持久化的不是聚合根的最新状态,而是聚合根产生的领域事件.最近我在思考如何实现一个基于文件的eventstore.目标有 ...
- CXF 入门:创建一个基于WS-Security标准的安全验证(CXF回调函数使用,)
http://jyao.iteye.com/blog/1346547 注意:以下客户端调用代码中获取服务端ws实例,都是通过CXF 入门: 远程接口调用方式实现 直入正题! 以下是服务端配置 ==== ...
- 开源一个基于nio的java网络程序
因为最近要从公司离职,害怕用nio写的网络程序没有人能看懂(或许是因为写的不好吧),就调整成了mina(这样大家接触起来非常方便,即使没有socket基础,用起来也不难),所以之前基于nio写的网络程 ...
- 一个基于.NET平台的自动化/压力测试系统设计简述
AutoTest系统设计概述 AutoTest是一个基于.NET平台实现的自动化/压力测试的系统,可独立运行于windows平台下,支持分布式部署,不需要其他配置或编译器的支持.(本质是一个基于协议的 ...
- [转]一个基于完成端口的TCP Server Framework,浅析IOCP
[转]一个基于完成端口的TCP Server Framework,浅析IOCP http://www.cppblog.com/adapterofcoms/archive/2010/06/26/1187 ...
- 一个基于MVVM的TableView组件化实现方案
AITableView https://github.com/chentoo/AITableView cocoapods: pod ‘AITableView’ 做什么用? 这是一个简化UITableV ...
- 构建一个基于 Spring 的 RESTful Web Service
本文详细介绍了基于Spring创建一个“hello world” RESTful web service工程的步骤. 目标 构建一个service,接收如下HTTP GET请求: http://loc ...
- 一个基于STSdb和fastJson的磁盘/内存缓存
一个基于STSdb和fastJson的磁盘/内存缓存 需求 业务系统用的是数据库,数据量大,部分只读或相对稳定业务查询复杂,每次页面加载都要花耗不少时间(不讨论异步),觉得可以做一下高速缓存,譬如用n ...
随机推荐
- 【C】C语言位域(位段)详解
作者:李春港 出处:https://www.cnblogs.com/lcgbk/p/14215449.html 目录 一.位域是什么? 二.位域的存储 2.1 相邻成员的类型相同 2.2 相邻成员的类 ...
- WebService的开发手段
一.WebService的开发手段 目前有关webService的开发手段有2种 1.JDK开发(jdk必须是1.6及以上版本,因为jdk是在1.6版本中引入并支持webservice开发的); 2. ...
- 茅坑杀手与Alias Method离散采样
说起Alias,你可能第一个联想到的是Linux中的Alias命令,就像中世纪那些躲在茅坑下面(是真的,起码日本有粪坑忍者,没有马桶的年代就是社会的噩梦)进行刺杀的杀手一样,让人防不胜防,对于那些被这 ...
- springboot源码解析-管中窥豹系列之排序(五)
一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
- springboot异常处理之404
ps: 推荐一下本人的通用后台管理项目crowd-admin 以及newbee-mall增强版,喜欢的话给个star就好 源码分析 在springboot中默认有一个异常处理器接口ErrorConto ...
- UnityToLaya小插件-找出空格并替换
unity导出的文件中经常会出现带有空格的节点或者文件夹 而这些空格在本地开发测试过程中不会出现,当这些带有空格路径的文件需要放到网络上时,就出现问题了 所以这里写了一个简单的查找并清理空格的插件, ...
- 【JavaWeb】HTML&CSS 基础
HTML&CSS 基础 HTML 基础 HTML 标签 HTML标题:HTML 标题(Heading)是通过 h1 - h6 等标签进行定义的. HTML段落: HTML 段落是通过 p 标签 ...
- HBASE Shell基本命令
定义 HBASE是一种分布式.可扩展.支持海量数据存储的NoSQL数据库. HBASE数据模型 逻辑上,HBASE的数据模型同关系型数据库类似,数据存储到一张表中,有行有列,但是从HBASE的底层物理 ...
- mmall商城分类模块总结
后台分类model的开发具体功能有:添加分类名称,修改分类名称,查询所有子分类,查询父分类以及它下面的子分类(递归) 需要注意的是,在后台管理进行操作的时候,都需要验证当前用户是否是管理员的角色,不管 ...
- yum -y install gnuplot
[root@test~]# yum -y install gnuplotLoaded plugins: fastestmirrorLoading mirror speeds from cached h ...