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);
}

从上面的代码可能大概能看出一些实现,重写了默认的 EqualsGetHashCode,并重载了“==” 运算符,并且实现了一个从 StringValueDictionaryDictionary 的隐式转换,来看下面的实现代码:

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(复制、克隆)的目标

实现相等性比较的时候,EqualsGetHashCode 方法也要重写,如果没有重写 GetHashCode,编译器也会给出警告,如果没有重写 GetHashCode 在实际在 HashSet 或者 Dictionary 里可能会出现重复 key

重载运算符的时候需要一个静态方法,"==" 和 "!=" 是一对操作运算符,如果要实现两个都要实现,不能只实现其中一个

References

C# 实现一个基于值相等性比较的字典的更多相关文章

  1. 一个基于mysql构建的队列表

    通常大家都会使用redis作为应用的任务队列表,redis的List结构,在一段进行任务的插入,在另一端进行任务的提取. 任务的插入 $redis->lPush("key:task:l ...

  2. 关于实现一个基于文件持久化的EventStore的核心构思

    大家知道enode框架的架构是基于ddd+event sourcing的思想.我们持久化的不是聚合根的最新状态,而是聚合根产生的领域事件.最近我在思考如何实现一个基于文件的eventstore.目标有 ...

  3. CXF 入门:创建一个基于WS-Security标准的安全验证(CXF回调函数使用,)

    http://jyao.iteye.com/blog/1346547 注意:以下客户端调用代码中获取服务端ws实例,都是通过CXF 入门: 远程接口调用方式实现 直入正题! 以下是服务端配置 ==== ...

  4. 开源一个基于nio的java网络程序

    因为最近要从公司离职,害怕用nio写的网络程序没有人能看懂(或许是因为写的不好吧),就调整成了mina(这样大家接触起来非常方便,即使没有socket基础,用起来也不难),所以之前基于nio写的网络程 ...

  5. 一个基于.NET平台的自动化/压力测试系统设计简述

    AutoTest系统设计概述 AutoTest是一个基于.NET平台实现的自动化/压力测试的系统,可独立运行于windows平台下,支持分布式部署,不需要其他配置或编译器的支持.(本质是一个基于协议的 ...

  6. [转]一个基于完成端口的TCP Server Framework,浅析IOCP

    [转]一个基于完成端口的TCP Server Framework,浅析IOCP http://www.cppblog.com/adapterofcoms/archive/2010/06/26/1187 ...

  7. 一个基于MVVM的TableView组件化实现方案

    AITableView https://github.com/chentoo/AITableView cocoapods: pod ‘AITableView’ 做什么用? 这是一个简化UITableV ...

  8. 构建一个基于 Spring 的 RESTful Web Service

    本文详细介绍了基于Spring创建一个“hello world” RESTful web service工程的步骤. 目标 构建一个service,接收如下HTTP GET请求: http://loc ...

  9. 一个基于STSdb和fastJson的磁盘/内存缓存

    一个基于STSdb和fastJson的磁盘/内存缓存 需求 业务系统用的是数据库,数据量大,部分只读或相对稳定业务查询复杂,每次页面加载都要花耗不少时间(不讨论异步),觉得可以做一下高速缓存,譬如用n ...

随机推荐

  1. [LeetCode]92. Reverse Linked List II反转部分链表

    /* 重点还是反转链表 思路就是中间的反转,然后两头接上 */ public ListNode reverseBetween(ListNode head, int m, int n) { if (he ...

  2. JVM 低延迟垃圾收集器 Shenandoah 和 ZGC

    本文部分摘自<深入理解 Java 虚拟机第三版> 概述 衡量垃圾收集器的三项指标分别是:内存占用.吞吐量和延迟.这三者共同构成一个"不可能三角",即一款优秀的收集器最多 ...

  3. 解决MySQL Workbench导出低版本MySQL时报错Unknown table ‘column_statistics’ in information_schema的问题

    在使用高版本MySQL Workbench或MySQL 8.0+版本提供的mysqldump.exe(实际高版本的MySQL Workbench使用的也是高版本的mysqldump.exe)来导出低于 ...

  4. U盘容量变小处理

    参考: https://blog.csdn.net/weixin_39792252/article/details/80676300?utm_medium=distribute.pc_relevant ...

  5. instanceof和isInstance的区别

    instanceof 是一个操作符(类似new, ==等) ( Object reference variable ) instanceof (class/interface type) if(a i ...

  6. js原型链原理

    先附上原型链的图,能看懂的本文就没必要看了,看不懂的可以带着疑问看文章 一.构造函数 什么是构造函数:当一个普通函数创建一个类对象是,那么就程它为构造函数. 特点: 默认首字母大写 使用new关键字来 ...

  7. Java基础-数据类型及变量

    Java基本语法 1.标识符(zhi) 含义:名字 类名.对象名.方法名.变量名.常量名-- 一个合法的标识符的组成:数字.字母._和$ 注意事项: 不能重复 不能以数字开头 区分大小写 不能以关键字 ...

  8. LeetCode226 翻转二叉树

    翻转一棵二叉树. 示例: 输入: 4 / \ 2 7 / \ / \ 1 3 6 9 输出: 4 / \ 7 2 / \ / \ 9 6 3 1 备注:这个问题是受到 Max Howell的 原问题  ...

  9. docker迁入迁出mysql

    docker迁出mysql数据库 测试环境: docker服务器 mysql服务器 IP 192.168.163.19 192.168.163.16 操作系统 CentOS7.8 CentOS7.8 ...

  10. 了解一下IO控制器与控制方式

    IO控制器 CPU无法直接控制IO设备的机械部件,因此IO设备还要有个电子部件作为CPU和IO设备机械部件之间的"中介",用于实现CPU对设备的控制. 这个电子部件就是IO控制器, ...