【问题标题】:Can I implement a base GetHashCode() that will work for all extensions of my abstract base class?我可以实现一个适用于我的抽象基类的所有扩展的基本 GetHashCode() 吗?
【发布时间】:2012-09-26 19:12:13
【问题描述】:

我阅读了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(),但至少这样我不必为每个类手动编写实现。

【问题讨论】:

  • 似乎涉及散列反射会很慢
  • 接下来,我又阅读了一些关于 GetHashCode() 和 Equals() 的实现和使用以及它们之间的关系的内容。在获得了更好的理解之后,我放弃了上面我正在处理的基础 GetHashCode()。排队其中一个旧的 NBC 公益广告:你知道的越多……
  • structs 中覆盖GetHashCode() 总是好的原因之一正是为了避免使用与此相同的一般概念的默认值。它大部分时间都可以工作,但它比大多数简单的GetHashCode() 覆盖要慢得多,您可以在不到一分钟的时间内编写代码。

标签: c# abstract gethashcode


【解决方案1】:

这是否比以下表现更好:

public override int GetHashCode()
{
    return 1;
}

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

进行基准测试是值得的。

另外,当 Equals 返回 true 时,哈希码必须相等,所以所有子类在其 Equals 方法中只使用公共属性吗?如果没有,您可能会考虑遍历所有属性,而不仅仅是公共属性。

编辑添加: 此外,请考虑在您的属性循环周围添加unchecked,以防止在哈希值大于 Int.MaxValue 时出现异常。

【讨论】:

  • +1 尤其是“哈希码匹配等于”备注。请注意,反射本身不会使 GetHashCode 变慢,因为如果没有对对象进行任何更改,结果可以被缓存。问题是确保对象在调用之间不发生变化是很重要的。
  • 感谢您对性能的批评以及关于unchecked 的注释。在我的情况下,我不会处理足够多的对象(可能是几千个实例)以显着影响性能。尽管如此,我还是要回去重新考虑我前进的方向。
【解决方案2】:

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

【讨论】:

  • -1 - 这不是真的。如果您查看msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx,它明确表示“必须始终返回相同的哈希码,只要对象状态没有修改
  • Eric Lippert 写道:“规则:当对象包含在依赖于哈希码保持稳定的数据结构中时,GetHashCode 返回的整数绝不能更改”。他还写道,指导方针是结果永远不会改变。对于他的完整解释,请阅读原帖:blogs.msdn.com/b/ericlippert/archive/2011/02/28/…
  • 如果哈希是从对象的状态派生的,GetHashCode() 的结果不可能在对象的整个生命周期内保持不变。该规则将使使用对象状态生成哈希码的每个示例都无效。根据规则,从构建开始的那一刻起,您就不能更改任何用于派生哈希码的值。
  • 不完全是,它只是意味着您应该根据不可变成员生成哈希码...
  • GetHashCode() 必须如果对象发生更改以使其不再等于之前相等的对象,或者不再等于对象它以前不相等。如果这会导致问题(例如,对象在更改时被用作字典的键),那么问题在于更改或它首先被用于非引用相等的事实,而不是事实GetHashCode() 返回的值已更改。
【解决方案3】:

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

【讨论】:

  • "在大多数情况下,您知道何时需要它。"哈!那正是我所想!现在我发现,当我使用 DevExpress XtraGrid(一个类似网格的控件)并将其绑定到我的对象的 List 时,DevExpress 代码有时会在内部使用我的对象作为键创建一个 Hashtable。然后它崩溃了。呸!
猜你喜欢
  • 2012-05-03
  • 1970-01-01
  • 1970-01-01
  • 2013-12-24
  • 1970-01-01
  • 1970-01-01
  • 2010-09-21
  • 1970-01-01
  • 2015-12-24
相关资源
最近更新 更多