我可以实现一个基本的GetHashCode(),它可以用于我的抽象基类的所有扩展吗?

时间:2012-09-26 19:12:13

标签: c# abstract gethashcode

我读了this question here,这导致我this article here

我有一个抽象基类,它允许我约束方法只接受扩展我的抽象基类(基本多态)的类。我的问题是:我可以在我的抽象基类中实现GetHashCode(),以便为任何具体实现提供合适的覆盖吗? (即,要避免在每个具体类中覆盖GetHashCode()。)

我在想象我的抽象基类中的方法是这样的:

public abstract class FooBase
{
    private static readonly int prime_seed = 13;
    private static readonly int prime_factor = 7;

    public override int GetHashCode()
    {
        // Seed using the hashcode for this concrete class' Type so
        // two classes with the same properties return different hashes.
        int hash = prime_seed * this.GetType().GetHashCode();
        // Get this concrete class' public properties.
        var props = this.GetType().GetProperties(BindingFlags.Public);
        foreach (var prop in props)
        {
            // Factor in each of this concrete class' public properties' hashcodes.
            hash = (hash * prime_factor) + prop.GetHashCode();
        }
        return hash;
    }
}

这似乎适用于一些基本的平等单元测试,但我觉得我忽略了一些东西。我仍然必须在每个具体类中提供一个覆盖,以避免编译器警告有关不覆盖GetHashCode(),但至少这样我不必为每个类手动编写实现。

3 个答案:

答案 0 :(得分:2)

这是否比以下表现更好:

public override int GetHashCode()
{
    return 1;
}

哈希函数的关键是计算速度必须快。通过反射,您可能会失去通过哈希获得的所有好处。

基准测试是值得的。

此外,当Equals返回true时,哈希码必须相等,那么所有子类在其Equals方法中是否只使用公共属性?如果没有,您可以考虑循环所有属性,而不仅仅是公共属性。

编辑添加: 另外,考虑在属性循环周围添加unchecked以防止异常(如果散列大于Int.MaxValue)。

答案 1 :(得分:1)

您错过了一个基本规则 - GetHashCode()结果必须在对象的整个生命周期内保持不变。在您的GetHashCode实现中,它不受保证(因为您正在迭代的属性可以是可变的)。

答案 2 :(得分:0)

使用反射相当慢,但在某些情况下它是可以接受的。当类的实例是哈希表或字典中的键时,使用GetHashCode函数。在大多数情况下,您知道何时需要它。有一些工具,例如Resharperexample)可以为您生成GetHashCode和Equals函数,而不是手动编写它们。