专栏首页林德熙的博客dotnet C# 实现 GetHashCode 的方法

dotnet C# 实现 GetHashCode 的方法

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

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

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

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

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

        public override int GetHashCode()
        {
            return RuntimeHelpers.GetHashCode(this);
        }

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

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

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

    public class Degree
    {
        public Degree(int value)
        {
            IntValue = value;
        }

        public int IntValue
        {
            get => _intValue;
            private set
            {
                var d = value % MaxDegreeValue;
                if (d < 0) d += MaxDegreeValue;

                _intValue = d;
            }
        }


        /// <inheritdoc />
        public override bool Equals(object obj)
        {
            if (obj == null || GetType() != obj.GetType())
            {
                return false;
            }

            var p = (Degree) obj;
            return IntValue == p.IntValue;
        }

        /// <inheritdoc />
        public override int GetHashCode()
        {
            return IntValue.GetHashCode();
        }

        private int _intValue;
    }

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

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

        private double Foo1 { get; }

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

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

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

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

    public readonly struct FooInfo
    {
        public string Name { get; }
        public string TextImageFile { get; }
        public string BackgroundImageFile { get; }
        public bool IsValid => File.Exists(TextImageFile) && File.Exists(BackgroundImageFile);

        public FooInfo(string name, string textImageFile, string backgroundImageFile)
        {
            Name = name ?? throw new ArgumentNullException(nameof(name));
            TextImageFile = textImageFile ?? throw new ArgumentNullException(nameof(textImageFile));
            BackgroundImageFile = backgroundImageFile ?? throw new ArgumentNullException(nameof(backgroundImageFile));
        }

        public void Deconstruct(out string name, out string textImageFile, out string backgroundImageFile)
        {
            name = Name;
            textImageFile = TextImageFile;
            backgroundImageFile = BackgroundImageFile;
        }

        public override bool Equals(object? obj)
        {
            return obj is FooInfo info &&
                string.Equals(Name, info.Name, StringComparison.Ordinal) &&
                string.Equals(TextImageFile, info.TextImageFile, StringComparison.OrdinalIgnoreCase) &&
                string.Equals(BackgroundImageFile, info.BackgroundImageFile, StringComparison.OrdinalIgnoreCase);
        }

        public override int GetHashCode()
        {
            var hashCode = -1405208737;
            hashCode = hashCode * -1521134295 + StringComparer.Ordinal.GetHashCode(Name);
            hashCode = hashCode * -1521134295 + StringComparer.OrdinalIgnoreCase.GetHashCode(TextImageFile);
            hashCode = hashCode * -1521134295 + StringComparer.OrdinalIgnoreCase.GetHashCode(BackgroundImageFile);
            return hashCode;
        }
    }

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

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

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


本文会经常更新,请阅读原文: https://blog.lindexi.com/post/dotnet-C-%E5%AE%9E%E7%8E%B0-GetHashCode-%E7%9A%84%E6%96%B9%E6%B3%95.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

如果你想持续阅读我的最新博客,请点击 RSS 订阅,推荐使用RSS Stalker订阅博客,或者前往 CSDN 关注我的主页

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

无盈利,不卖课,做纯粹的技术博客

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • dotNET Core 3.X 依赖注入

    如果说在之前的 dotNET 版本中,依赖注入还是个比较新鲜的东西,那么在 dotNET Core 中已经是随处可见了,可以说整个 dotNET Core 的框...

    oec2003
  • 为什么System.Attribute的GetHashCode方法需要如此设计?

    昨天我在实现《通过扩展改善ASP.NET MVC的验证机制[使用篇]》的时候为了Attribute 的一个小问题后耗费了大半天的精力,虽然最终找到了问题的症结并...

    蒋金楠
  • C# 对象哈希码

    FCL的设计者认为,如果能将任何对象的任何实例放到哈希集合中,能带来很多好处。为此,System.Object提供了GetHashCode,它能获取任何对象的I...

    郑小超.
  • 如何重写object虚方法

    在 C# 中 Object 是所有类的基类,所有的结构和类都直接或间接的派生自它。前面这段话可以说所有的 C# 开发人员都知道,但是我相信其中有一部分程序员并不...

    喵叔
  • 从系统性能优化谈对象相等性

    公司系统中有一接口访问量大,内部计算逻辑较为复杂。在优化时打算把Request中的参数做为Key,Response做为Value放到进程内缓存中,以降低服务器压...

    雪飞鸿
  • 理解C#语言中相等Equality 和唯一 Identity

    程序你好
  • .NET 中 GetHashCode 的哈希值有多大概率会相同(哈希碰撞)

    如果你试图通过 GetHashCode 得到的一个哈希值来避免冲突,你可能要失望了。因为实际上 GetHashCode 得到的只是一个 Int32...

    walterlv
  • 编写高质量代码改善C#程序的157个建议[10-12]

      本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html 。本文主要学习记录以下内容:

    aehyok
  • NHibernate联合主键详细示例

    使用NHibernate实现一对多,多对一的关联很是简单,可如果要用复合主键实现确实让人有些淡淡的疼。虽然很淡疼但还是要去抹平这个坑,在下不才,愿意尝试。 以示...

    sam dragon

扫码关注云+社区

领取腾讯云代金券