C#怎样实现一个基于值相等性比较的字典-创新互联

这篇文章主要介绍C#怎样实现一个基于值相等性比较的字典,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!

10年专注建站、设计、互联网产品按需网站设计服务,业务涵盖高端网站设计成都商城网站开发、微信小程序、软件系统开发、成都app开发等。凭借多年丰富的经验,我们会仔细了解每个客户的需求而做出多方面的分析、设计、整合,为客户设计出具风格及创意性的商业解决方案,创新互联更提供一系列网站制作和网站推广的服务,以推动各中小企业全面信息数字化,并利用创新技术帮助各行业提升企业形象和运营效率。

Intro

今天在项目里遇到一个需求,大概是这样的我要比较两个 JSON 字符串是不是相等,JSON 字符串其实是一个Dictionary 但是顺序可能不同,和上一篇 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()
 {
 {"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()
 {
 {"Id", 1},
 {"Name", "Tom" },
 });
 var set = new HashSet();
 set.Add(dic1);
 set.Add(dic2);

 Assert.Single(set);
}

[Fact]
public void CloneTest()
{
 var dic1 = StringValueDictionary.FromObject(new Dictionary()
 {
 {"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 dictionary = stringValueDictionary;
 Assert.Equal(stringValueDictionary.Count, dictionary.Count);

 var dic2 = StringValueDictionary.FromObject(dictionary);

 Assert.Equal(dic2, stringValueDictionary);
 Assert.True(dic2 == stringValueDictionary);
}

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

public sealed class StringValueDictionary : IEquatable
{
 private readonly Dictionary _dictionary = new();

 private StringValueDictionary(IDictionary 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 dictionary)
 {
  return new StringValueDictionary(dictionary);
 }
 if (obj is IDictionary 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>()
  .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.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(StringValueDictionary dictionary)
 {
 return dictionary._dictionary;
 }
}

More

上述代码实现的有点粗糙,可能会有一些问题,仅供参考

以上代码基本实现了基于想要的值的相等性比较以及 Clone(复制、克隆)的目标

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

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

implicit 也算是一个特殊的运算符,巧妙的使用隐式转换可以大大简化代码的写法,StackExchange.Redis 中就使用了implicit 来实现RedisValuestring 等其他常用类型的隐式转换

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#怎样实现一个基于值相等性比较的字典-创新互联
URL标题:http://hbruida.cn/article/ehcpg.html