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 ...
随机推荐
- ubuntu虚拟机启用双网卡IP配置
首先要登入自己的虚拟机,这里以ubuntu为例. 配置两块网卡,一块eth0为NAT模式,另一块为eth1仅主机模式 # 进入网卡配置页面vi /etc/network/interfaces # Th ...
- webapplicationContext之ServletContext等相关概念说明
1)ServletContext是一个全局的储存信息的空间,所有用户共用一个,其信息必须是线程安全且共享的. ServletContext有一个接口定义:ServletContext接口.此接口定义了 ...
- linux中的dmesg命令以及确定进程是否被系统主动kill
linux中的dmesg命令以及确定进程是否被系统主动kill Feb 21, 2017 | java | 185 Hits 近期发现线上项目的进程莫名其妙的就不见了,也没有崩溃日志,就怀疑是被操作系 ...
- Thymeleaf语法总结 | 笔记分享
Thymeleaf语法总结 一.Thymeleaf介绍 Thymeleaf是Spring boot推荐使用的模版引擎,直接以html显示,前后端可以很好的分离. 二.Thymeleaf语法(Thy ...
- 一个简单的springboot+mybatis-plus+thymeleaf的学生管理系统
一.登录功能 1.1登录所涉及的功能主要包括拦截器,过滤器,用户在未登录的时候,访问页面会阻止访问的,如图所示: 实现这个功能的主要代码如下所示 1 //拦截器 2 public class Logi ...
- 不想加班开发管理后台了,试试这个 Java 开源项目吧!
本文适合有 Java 基础并了解 SpringBoot 框架的同学 本文作者:HelloGitHub-嘉文 这里是 HelloGitHub 推出的<讲解开源项目>系列,今天给大家带来一款开 ...
- C#扫盲篇(一):反射机制--情真意切的说
在一线编码已有多年,积累了不少非常实用的技能,最近的更新会逐步的分享出来,希望能帮助到还有一丢丢喜欢.Net的朋友,当然这些都比较适合入门选手,虽然自己已是个精通抄代码的老猿,但技术造诣仍是渣渣. 犹 ...
- NOIP初赛篇——04计算机软件系统
计算机软件是指计算机系统中的程序及其文档,也是用户与硬件之间的接口,用户主要通过软件与计算机进行交流,软件是计算机的灵魂.没有安装软件的计算机称为"裸机",无法完成任何工作.一般软 ...
- fastjson反序列化漏洞原理及利用
重要漏洞利用poc及版本 我是从github上的参考中直接copy的exp,这个类就是要注入的类 import java.lang.Runtime; import java.lang.Process; ...
- 【C++】《C++ Primer 》第十八章
第十八章 用于大型程序的工具 大规模应用程序的特殊要求包括: 在独立开发的子系统之间协同处理错误的能力. 使用各种库进行协同开发的能力. 对比较复杂的应用概念建模的能力. 一.异常处理 异常处理(ex ...