在C#中实现GetHashCode。空值处理

时间:2015-07-03 12:33:02

标签: c# .net hash

在开始之前,我在Mono环境中对所有代码示例进行了测试,string.Empty.GetHashCode(); // returns 0 in Mono 3.10 string.Empty.GetHashCode(); // returns 757602046 in .NET 4.5.1 实现中存在一个显着差异:

public class Entity {
    public int EntityID { get; set; }
    public string EntityName { get; set; }

    public override int GetHashCode() {
        unchecked {
            int hash = 15485863;       // prime number
            int multiplier = 1299709;  // another prime number

            hash = hash * multiplier + EntityID.GetHashCode();
            hash = hash * multiplier + (EntityName != null ? EntityName.GetHashCode() : 0);

            return hash;
        }
    }
}

我根据@JonSkeet的这个SO Answer进行了实现,并且在评论中他还建议使用0哈希值来表示NULL值(我不确定如何对它们进行哈希)。

  

我通常使用0作为null的有效哈希码 - 这与忽略该字段不同。

所以拥有以下implementation(Mono 3.10):

var hash1 = new Entity { EntityID = 1337, EntityName = "" }.GetHashCode();
var hash2 = new Entity { EntityID = 1337, EntityName = null }.GetHashCode();

bool equals = hash1 == hash2; // true

很容易发现碰撞,例如

DelegateModel

我可以将null-value 0替换为其他一些数字,但是它不会解决问题,因为仍然有一些哈希(字符串)输出会产生这样的数字而我会得到另一个碰撞。

我的问题:在使用上面示例中的算法时,我应该如何处理空值?

2 个答案:

答案 0 :(得分:4)

  

我的问题:在使用算法时我应该如何处理空值   从上面的例子?

我认为问题不在于null本身。问题在于你使用GetHashCode来表示相等,这并不意味着它。 GetHashCode应提供渴望正常分配的哈希值。

The docs say

  

两个相等的对象返回相等的哈希码。然而,   反之则不然:相等的哈希码并不意味着对象   相等,因为不同(不相等)的对象可以具有相同的哈希值   码。

然后继续指定GetHashCode的目的:

  

哈希码用于高效插入和查找   基于哈希表的集合。

您应该实现IEquatable<Entity>,其中您实际定义了两个实体的等价关系。并且当你在它的时候覆盖!===

近似值:

public class Entity : IEquatable<Entity>
{
    public int EntityId { get; set; }
    public string EntityName { get; set; }

    public bool Equals(Entity other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return EntityId == other.EntityId && 
               string.Equals(EntityName, other.EntityName);
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != this.GetType()) return false;
        return Equals((Entity) obj);
    }

    public static bool operator ==(Entity left, Entity right)
    {
        return Equals(left, right);
    }

    public static bool operator !=(Entity left, Entity right)
    {
        return !Equals(left, right);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            return (EntityId*397) ^ (EntityName != null ? EntityName.GetHashCode() : 0);
        }
    }
}

答案 1 :(得分:3)

这里的“问题”是您正在尝试获取无冲突的哈希码。虽然这对于使用哈希代码进行查找的集合实现(例如HashSetDictionary)的查找性能来说是完美的,但在大多数情况下这都不起作用。

原因是哈希码只是一个32位整数值,它表示通常更大的数据(多个整数值,字符串等)。

因此哈希码只用于定义两个对象可以相等。集合类使用哈希代码来优化存储对象的区域,并使用equals函数来查找两个对象是否真的相同。因此,您应该始终为实现哈希代码的类实现Equals函数。虽然这些类将回退到对象的equals函数,但是实现IEquatable<T>接口以避免键入任何类型的问题(仍然覆盖Object的默认equals方法)也是一个好主意。