关于哈希码的不可变类型是否有任何考虑?
我应该在构造函数中生成一次吗?
您如何明确哈希码是固定的?我是不是该?如果是这样,使用名为 HashCode 的属性而不是 GetHashCode 方法会更好吗?会有什么缺点吗?(考虑到两者都可以,但建议使用该属性)。
关于哈希码的不可变类型是否有任何考虑?
我应该在构造函数中生成一次吗?
您如何明确哈希码是固定的?我是不是该?如果是这样,使用名为 HashCode 的属性而不是 GetHashCode 方法会更好吗?会有什么缺点吗?(考虑到两者都可以,但建议使用该属性)。
关于哈希码的不可变类型是否有任何考虑?
不可变类型是最容易正确散列的类型;大多数哈希码错误发生在对可变数据进行哈希处理时。最重要的是哈希和相等是一致的;如果两个实例比较相等,则它们应该具有相同的哈希码。(反过来不一定正确;具有相同散列的两个实例不必相等。)
我应该在构造函数中生成一次吗?
这是一种性能优化技术;通过这样做,您可以用增加的空间消耗(用于存储计算值)换取可能的时间减少。我从不进行性能优化,除非它们是由现实的、以客户为中心的性能测试驱动的,这些测试根据记录的目标仔细衡量两个选项的性能。如果您精心设计的实验表明 (1) 不这样做会导致您错过目标,并且 (2) 这样做会导致您实现目标,那么您应该这样做。
您如何明确哈希码是固定的?
我不明白这个问题。不断变化的哈希码是例外,而不是规则。哈希码总是应该是不变的。如果对象的哈希码发生变化,那么该对象可能会在哈希表中“丢失”,因此每个人都应该假设哈希码保持稳定。
使用名为 HashCode 的属性而不是 GetHashCode 方法会更好吗?
您的对象的哪个消费者会说“好吧,我可以调用 GetHashCode(),这是一种保证适用于所有对象的方法,但我将调用这个 HashCode getter,它做同样的事情”?你心里有这样的消费者吗?
如果您没有任何功能消费者,则不要提供该功能。
我通常不会在构造函数中生成它,但在决定是否缓存它之前,我还想了解更多关于预期用途的信息。
您是否期待少数实例被大量散列并且需要很长时间来计算散列?如果是这样,缓存可能是合适的。如果您期待大量潜在的“丢弃”实例,我不会打扰缓存。
有趣的是,.NET 和 Java 在这方面对 String 做出了不同的选择——Java 缓存哈希,.NET 没有。鉴于许多字符串实例从未被散列,而那些被散列的实例通常只被散列一次(例如在插入散列表时),我认为我赞成.NET 在这里的决定。
基本上你是在用内存+复杂性来换取速度。正如迈克尔所说,在使您的代码更复杂之前进行测试。当然,在某些情况下(例如对于类库),您无法准确预测实际使用情况,但在许多情况下,您会有一个不错的主意。
不过,您当然不需要单独的财产。除非有人更改对象的状态,否则哈希码应始终保持不变 - 如果您的类型是不可变的,则您已经禁止这样做,因此用户不应期望任何更改。只需覆盖GetHashCode()
.
我会getHashCode
在第一次调用时生成一次哈希码,然后将其缓存以供以后调用。这样可以避免在不需要时在构造函数中调用它。
如果您不希望getHashCode
为每个值对象调用很多次,则可能根本不需要缓存该值。
好吧,您必须有一个 GetHashCode() 覆盖方法,因为这就是消费者检索您的哈希码的方式。大多数哈希码都是相当简单的算术运算,可以快速执行。您是否有理由相信缓存结果(有内存成本)会给您带来显着的性能提升?
从简单开始 - 即时生成哈希码。如果您认为缓存它会看到性能改进,请先进行测试。
法规要求我在这一点上参考“过早的优化是万恶之源”的报价。
根据我的个人经验,我知道开发人员非常擅长误判性能问题。
因此,建议在 GetHashCode() 中动态计算哈希码时,让一切尽可能简单。
一般来说,计算 HashCode 应该很快。所以缓存不应该是一种优化,也不值得麻烦。
如果分析确实表明 GethashCode 需要大量时间,那么也许您应该缓存它,作为修复。
但我不认为这是正常做法的一部分。
为什么需要确保哈希码是固定的?哈希码的语义是对于对象的任何给定状态,它始终是相同的值。由于您的对象是不可变的,因此这是给定的。如何选择实现 GetHashCode 由我们决定。
让它成为一个返回的私有字段是一种选择——它小巧、简单、快速。