如何在.net(c#)中为可以安全存储在数据库中的字符串创建哈希代码?

如何在.net(c#)中为可以安全存储在数据库中的字符串创建哈希代码?,c#,.net,database,hashcode,gethashcode,C#,.net,Database,Hashcode,Gethashcode,引用Eric Lippert的话: 规则:GetHashCode的使用者不能依赖它随时间或跨appdomains保持稳定 假设您有一个客户对象 有很多字段,比如Name, 地址等等。如果你做两个 这样的物体具有完全相同的形状 数据在两个不同的过程中,它们 不必返回相同的散列 代码。如果你把这样一个物体放在 周二在一个过程中,关闭它, 然后在上再次运行该程序 星期三,散列码可以是 不一样 这在过去曾经咬过人。 文件 System.String.GetHashCode注释 具体地说,两个相同的 字符

引用Eric Lippert的话:

规则:GetHashCode的使用者不能依赖它随时间或跨appdomains保持稳定

假设您有一个客户对象 有很多字段,比如Name, 地址等等。如果你做两个 这样的物体具有完全相同的形状 数据在两个不同的过程中,它们 不必返回相同的散列 代码。如果你把这样一个物体放在 周二在一个过程中,关闭它, 然后在上再次运行该程序 星期三,散列码可以是 不一样

这在过去曾经咬过人。 文件 System.String.GetHashCode注释 具体地说,两个相同的 字符串可以有不同的哈希代码 在不同版本的CLR中,以及 事实上,他们是这样做的不要在数据库中存储字符串哈希,并希望它们永远保持不变,因为它们不会保持不变。

那么,创建可以存储在数据库中的字符串哈希代码的正确方法是什么呢


(请告诉我,我不是第一个在我编写的软件中留下这个bug的人!)

答案是只编写自己的哈希函数。你可以通过在你发表的文章评论中的链接找到一些文章的来源。或者,您可以使用一个原本用于加密的内置哈希函数(MD5、SHA1等),而不使用所有的位。

这取决于您希望该哈希具有什么属性。例如,您可以这样写:

public int HashString(string text)
{
    // TODO: Determine nullity policy.

    unchecked
    {
        int hash = 23;
        foreach (char c in text)
        {
            hash = hash * 31 + c;
        }
        return hash;
    }
}
只要您记录了哈希的计算方式,这是有效的。它绝不是加密安全的或类似的,但您可以毫无问题地坚持它。顺序意义上绝对相等的两个字符串(即不应用文化平等等,每个字符完全相同)将生成与此代码相同的哈希

当您依赖于未记录的哈希时,就会出现问题-即,某些东西遵循
GetHashCode()
,但不能保证在不同版本之间保持不变。。。比如
string.GetHashCode()

像这样编写和记录您自己的散列有点像说,“这个敏感信息是用MD5(或其他什么)散列的”。只要它是定义良好的散列,就可以了


编辑:其他答案建议使用加密哈希,如SHA-1或MD5。我想说的是,在我们知道密码安全性而不仅仅是稳定性的要求之前,将字符串转换为字节数组并对其进行散列的繁琐过程是没有意义的。当然,如果散列要用于任何与安全相关的内容,那么行业标准的散列正是您应该使用的。但在问题的任何地方都没有提到这一点。

这里是的重新实现。这不使用像real
GetHashCode()
那样的指针,因此速度会稍微慢一些,但它确实使它对
string
的内部更改更具弹性,这将提供更均匀分布的哈希代码,而这可能会导致字典中更好的查找时间

public static class StringExtensionMethods
{
    public static int GetStableHashCode(this string str)
    {
        unchecked
        {
            int hash1 = 5381;
            int hash2 = hash1;

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

            return hash1 + (hash2*1566083941);
        }
    }
}
公共静态类StringExtensionMethods
{
公共静态int GetStableHashCode(此字符串为str)
{
未经检查
{
int hash1=5381;
int hash2=hash1;
对于(int i=0;ihash1=((hash1,我从不依赖GetHashCode,因为我知道,我实现这个方法有多马虎。我相信其他人做得并没有更好…;-)您不是第一个在您编写的软件中留下此错误的人。Dbase引擎已经非常擅长对字符串进行哈希运算。只需为列创建一个索引即可。@Hans,请看,不要假设字符串存储在一个表中。这有什么关系?无论如何,您都要为联接中使用的列编制索引,以加快查询速度。在我看来,您正在尝试执行dbase引擎的工作。23和
*31
有什么神奇之处吗?更确切地说,有什么理由选择这些值而不是任何其他值?…而不是任何其他[记录]散列法?我猜不是,尽管31比ASCII可打印文件少一个让我产生了不必要的怀疑。@ruffin:它们是Josh Bloch推荐的值。乘以31是有效的,因为它可以作为移位和减法。关于这一点,还有很多其他问题在讨论——老实说,这有点像一门黑暗的艺术。整洁!From:选择值31是因为它是奇数素数。如果它是偶数,乘法溢出,信息将丢失,因为乘法等于移位。使用素数的优点不太清楚,但它是传统的。31的一个很好的特性是乘法可以用移位和减法替换更好的性能:
31*i==(i“现代虚拟机自动完成这种优化”这也适用于.NET吗?回答得好!我喜欢“自己动手,我一点线索都没有”的精神