.NET Core应用程序每次启动后使用string.GetHashCode()方法获取到的哈希值(hash)不相同

前言

如标题所述,在ASP.NET Core应用程序中,使用string.GetHashCode()方法去获取字符串的哈希值,但每次重启这个ASP.NET Core应用程序之后,同样的字符串的哈希值(hash)但不相同了。这是什么意思呢?

具体的应用场景是这样的: 项目中有一张表的某个字段保存了类似URL这样的字符串,这张表的数据量比较大,而且这个字段会被经常用作检索。所以,为了提高查询效率,在存储的时候同时存储了这个URL字段的哈希值(hash)。

原来ASP.NET MVC 5项目的.NET Framework版本中,随便应用程序怎么重启,使用string.GetHashCode()方法获取到的哈希值永远都是一致的(也需要保持一致)。

但在ASP.NET Core 应用程序中,使用string.GetHashCode()方法,就出现了上述的问题,导致程序在相应的功能部分也出现Bug。最终经过多次测试和查阅资料,定位到了问题的原因。

问题重现

这里我重现一下问题的场景,以下是.NET Framework 4.6.1版本的string.GetHashCode()方法在不断重启程序之后获取到的哈希值(hash),每次获取到的都是相同的值:

using System;

namespace ConsoleApp2
{
    class Program
    {
        static void Main()
        {
            var str = "Hello world";
            Console.WriteLine(str.GetHashCode());
            Console.ReadKey();
        }
    }
}

输出:-1660742776

但是,如果在.NET Core应用程序中执行以上相同的代码,每次重启程序后获取到的哈希值是不相同的。

比如我重启测试了三次.NET Core应用程序,分别得到了三个不同的哈希值:313140527-11693814872141605647

在定位到问题之后,我们得找出生成不同哈希值的原因。经过一番努力查找之后,其实微软官方文档给出过使用GetHashCode()方法的建议(文档地址)。其中也明确提示了,不应该将使用.NET内置的GetHashCode()方法获取到的值作为持久化的值。

Hash codes are used to insert and retrieve keyed objects from hash tables efficiently. However, hash codes don’t uniquely identify strings. Identical strings have equal hash codes, but the common language runtime can also assign the same hash code to different strings. In addition, hash codes can vary by version of .NET, by platform within a single version, and by application domain. Because of this, you should not serialize or persist hash code values, nor should you use them as keys in a hash table or dictionary.

如何获取一个恒定的哈希值(hash)

那要确保.NET Framework和.NET Core应用程序获取到的哈希值都是相同的,我们需要自定义实现哈希值的算法,以下列出一个可行的算法:

public static class HashHelper
{
    public static int GetDeterministicHashCode(this string str)
    {
        unchecked
        {
            int hash1 = (5381 << 16) + 5381;
            int hash2 = hash1;

            for (int i = 0; i < str.Length; i += 2)
            {
                hash1 = ((hash1 << 5) + hash1) ^ str[i];
                if (i == str.Length - 1)
                    break;
                hash2 = ((hash2 << 5) + hash2) ^ str[i + 1];
            }

            return hash1 + (hash2 * 1566083941);
        }
    }
}

完整调用示例(.NET Core):

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var str = "Hello world";
            Console.WriteLine(str.GetDeterministicHashCode());
            Console.WriteLine(str.GetDeterministicHashCode());
            Console.WriteLine(str.GetDeterministicHashCode());
            Console.WriteLine("****************************");
            Console.WriteLine(str.GetHashCode());
            Console.WriteLine(str.GetHashCode());
            Console.WriteLine(str.GetHashCode());
            Console.ReadKey();
        }
    }

    public static class HashHelper
    {
        public static int GetDeterministicHashCode(this string str)
        {
            unchecked
            {
                int hash1 = (5381 << 16) + 5381;
                int hash2 = hash1;

                for (int i = 0; i < str.Length; i += 2)
                {
                    hash1 = ((hash1 << 5) + hash1) ^ str[i];
                    if (i == str.Length - 1)
                        break;
                    hash2 = ((hash2 << 5) + hash2) ^ str[i + 1];
                }

                return hash1 + (hash2 * 1566083941);
            }
        }        
    }
}

 

posted @ 2022-01-21 18:03  拼博之路  阅读(491)  评论(0编辑  收藏  举报