飞道的博客

dotnet C# 实现 GetHashCode 的方法

425人阅读  评论(0)

本文来聊聊在重写某个类的 GetHashCode 方法时,可以如何实现 GetHashCode 的返回值

按照 GetHashCode 方法的原则,要求两个对象如果 Equals 返回 true 那么一定要求 GetHashCode 也返回相同的值。当然,反过来不成立,也就是两个对象返回的 GetHashCode 的值相同,对象可以是不相等的

实现 GetHashCode 方法的方式有很多,最简单的就是通过调用基类的 GetHashCode 方法,代码如下


   
  1. public override int GetHashCode()
  2. {
  3. return base.GetHashCode();
  4. }

第二个方法就是通过 RuntimeHelpers 静态类的 GetHashCode 方法,代码如下


   
  1. public override int GetHashCode()
  2. {
  3. return RuntimeHelpers.GetHashCode(this);
  4. }

如果调用的 base.GetHashCode 的 base 是 object 类型的,也就是调用了 object 的 GetHashCode 方法,其实和调用 RuntimeHelpers 的 GetHashCode 方法是相同的,因为在 object 方法里面的 GetHashCode 定义如下


   
  1. // GetHashCode is intended to serve as a hash function for this object.
  2. // Based on the contents of the object, the hash function will return a suitable
  3. // value with a relatively random distribution over the various inputs.
  4. //
  5. // The default implementation returns the sync block index for this instance.
  6. // Calling it on the same object multiple times will return the same value, so
  7. // it will technically meet the needs of a hash function, but it's less than ideal.
  8. // Objects (& especially value classes) should override this method.
  9. public virtual int GetHashCode()
  10. {
  11. return RuntimeHelpers.GetHashCode(this);
  12. }

如果某个类型只有一个字段,期望是作为此字段的包装,那么可以通过返回此字段的 GetHashCode 的值


   
  1. public class Degree
  2. {
  3. public Degree( int value)
  4. {
  5. IntValue = value;
  6. }
  7. public int IntValue
  8. {
  9. get => _intValue;
  10. private set
  11. {
  12. var d = value % MaxDegreeValue;
  13. if (d < 0) d += MaxDegreeValue;
  14. _intValue = d;
  15. }
  16. }
  17. /// <inheritdoc />
  18. public override bool Equals( object obj)
  19. {
  20. if (obj == null || GetType() != obj.GetType())
  21. {
  22. return false;
  23. }
  24. var p = (Degree) obj;
  25. return IntValue == p.IntValue;
  26. }
  27. /// <inheritdoc />
  28. public override int GetHashCode()
  29. {
  30. return IntValue.GetHashCode();
  31. }
  32. private int _intValue;
  33. }

如上面代码,返回的就是 IntValue 的 GetHashCode 的值

而如果期望有自己的定制化,可以通过 HashCode 结构体实现定义,例如在 Program 类里面有属性定义如下

        private double Foo1 { get; }

此时如需要将 Foo1 属性加入到 HashCode 可以使用如下代码


   
  1. var hashCode = new HashCode();
  2. hashCode.Add(Foo1);
  3. return hashCode.ToHashCode();

在 HashCode 里面将会自动加上一套有趣的机制将传入的多个属性或字段计算出 HashCode 值

如果 HashCode 做不到自己需要的特殊需求,也可以自己动手,毕竟只要返回一个 int 值就可以,只要两个相等的对象返回的 int 值是相同的就没锅


   
  1. public readonly struct FooInfo
  2. {
  3. public string Name { get; }
  4. public string TextImageFile { get; }
  5. public string BackgroundImageFile { get; }
  6. public bool IsValid => File.Exists(TextImageFile) && File.Exists(BackgroundImageFile);
  7. public FooInfo( string name, string textImageFile, string backgroundImageFile)
  8. {
  9. Name = name ?? throw new ArgumentNullException(nameof(name));
  10. TextImageFile = textImageFile ?? throw new ArgumentNullException(nameof(textImageFile));
  11. BackgroundImageFile = backgroundImageFile ?? throw new ArgumentNullException(nameof(backgroundImageFile));
  12. }
  13. public void Deconstruct(out string name, out string textImageFile, out string backgroundImageFile)
  14. {
  15. name = Name;
  16. textImageFile = TextImageFile;
  17. backgroundImageFile = BackgroundImageFile;
  18. }
  19. public override bool Equals( object? obj)
  20. {
  21. return obj is FooInfo info &&
  22. string.Equals(Name, info.Name, StringComparison.Ordinal) &&
  23. string.Equals(TextImageFile, info.TextImageFile, StringComparison.OrdinalIgnoreCase) &&
  24. string.Equals(BackgroundImageFile, info.BackgroundImageFile, StringComparison.OrdinalIgnoreCase);
  25. }
  26. public override int GetHashCode()
  27. {
  28. var hashCode = -1405208737;
  29. hashCode = hashCode * -1521134295 + StringComparer.Ordinal.GetHashCode(Name);
  30. hashCode = hashCode * -1521134295 + StringComparer.OrdinalIgnoreCase.GetHashCode(TextImageFile);
  31. hashCode = hashCode * -1521134295 + StringComparer.OrdinalIgnoreCase.GetHashCode(BackgroundImageFile);
  32. return hashCode;
  33. }
  34. }

以上代码的 IsValid 属性没有影响判断相等,因此可以忽略不计。而 TextImageFile 和 BackgroundImageFile 都是路径字符串,应该忽略大小写,但 Name 属性是区分大小写的,通过 StringComparer 静态类的辅助可以协助计算出值

上面代码的常数都是随意写的值

本文所有代码放在 githubgitee 欢迎小伙伴访问

我搭建了自己的博客 https://blog.lindexi.com/ 欢迎大家访问,里面有很多新的博客。只有在我看到博客写成熟之后才会放在csdn或博客园,但是一旦发布了就不再更新

如果在博客看到有任何不懂的,欢迎交流,我搭建了 dotnet 职业技术学院 欢迎大家加入

如有不方便在博客评论的问题,可以加我 QQ 2844808902 交流


本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接:http://blog.csdn.net/lindexi_gd ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系


转载:https://blog.csdn.net/lindexi_gd/article/details/117125242
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场