我在大多数equatable类型的GetHashCode实现中使用基于XOR的实现。
我已阅读several posts解释为什么它不是最佳解决方案所以我决定实施GetHashCode as suggested by Jon Skeet:
unchecked // Overflow is fine, just wrap
{
int hash = 17;
hash = hash * 23 + field1.GetHashCode();
hash = hash * 23 + field2.GetHashCode();
hash = hash * 23 + field3.GetHashCode();
return hash;
}
由于代码在大多数实现中可能类似,我尝试构建一个帮助器类来计算我所有类的哈希码。这应该是一件容易的事,但GetHashCode的一个主要限制是它必须快速。因此,任何涉及分配的实现都可能是不行的(例如,使用非静态类)。
理想情况下,对这种方法的调用如下:
public override GetHashCode() => HashCodeCalculator.Calculate(X, Y, Z);
拥有所有逻辑(未选中+ primes + null check ...)。但是使用params
参数会隐式创建一个数组。
最好是在每个类中复制散列算法吗?或者像以下一样高效的课程?
public static class HashCalculator
{
private const int _seed = 5923;
private const int _multiplier = 7481;
public static int Add(object value) => Add(_seed, value);
public static int Add(int current, object value)
{
int valueHashCode = (value != null) ? value.GetHashCode() : 0;
unchecked
{
return (current * _multiplier) + valueHashCode;
}
}
}
然后可以像这样使用:
public override int GetHashCode()
{
int result = HashCalculator.Add(Prop1);
result = HashCalculator.Add(result, Prop2);
return result;
}
答案 0 :(得分:3)
您可以为各种小的固定数量的参数(2,3,4等等)创建重载,直到您决定停止为止,以避免数组分配,然后只有params
重载当存在特别大量操作数时需要使用,此时数组分配的开销不太可能成为问题(因为它将是工作的较小百分比)完成)。
答案 1 :(得分:0)
我可以理解为什么使用某种辅助工具来计算哈希是如此诱人,但在这种情况下,效率与方便性相矛盾。你正试图吃一个饼干并吃它,答案取决于你愿意遗留多少饼干:)
int HashCode(params int subhashcodes)
但是调用它会很难看,因为你需要提供字段的哈希码作为参数。int
改为object
来调用你方法中的字段哈希码(我不完全确定在第一种情况下不会有拳击 - 随意纠正我)我个人会坚持用手写(或者用Resharper)。
答案 2 :(得分:0)
在基准测试之后,似乎使用像下面这样的结构几乎与XORing一样有效,并且很好地封装了哈希码计算。
/// <summary>
/// Calculates a hash code based on multiple hash codes.
/// </summary>
public struct HashCode
{
private const int _seed = 5923;
private const int _multiplier = 7481;
/// <summary>
/// Builds a new hash code.
/// </summary>
/// <returns>The built hash code.</returns>
public static HashCode Build() => new HashCode(_seed);
/// <summary>
/// Constructor from a hash value.
/// </summary>
/// <param name="value">Hash value.</param>
private HashCode(int value)
{
_value = value;
}
/// <summary>
/// Builds a new hash code and initializes it from a hash code source.
/// </summary>
/// <param name="hashCodeSource">Item from which a hash code can be extracted (using GetHashCode).</param>
public HashCode(object hashCodeSource)
{
int sourceHashCode = GetHashCode(hashCodeSource);
_value = AddValue(_seed, sourceHashCode);
}
private readonly int _value;
/// <summary>
/// Returns the hash code for a given hash code source (0 if the source is null).
/// </summary>
/// <param name="hashCodeSource">Item from which a hash code can be extracted (using GetHashCode).</param>
/// <returns>The hash code.</returns>
private static int GetHashCode(object hashCodeSource) => (hashCodeSource != null) ? hashCodeSource.GetHashCode() : 0;
/// <summary>
/// Adds a new hash value to a hash code.
/// </summary>
/// <param name="currentValue">Current hash value.</param>
/// <param name="valueToAdd">Value to add.</param>
/// <returns>The new hash value.</returns>
private static int AddValue(int currentValue, int valueToAdd)
{
unchecked
{
return (currentValue * _multiplier) + valueToAdd;
}
}
/// <summary>
/// Adds an object's hash code.
/// </summary>
/// <param name="hashCode">Hash code to which the object's hash code has to be added.</param>
/// <param name="hashCodeSource">Item from which a hash code can be extracted (using GetHashCode).</param>
/// <returns>The updated hash instance.</returns>
public static HashCode operator +(HashCode hashCode, object hashCodeSource)
{
int sourceHashCode = GetHashCode(hashCodeSource);
int newHashValue = AddValue(hashCode._value, sourceHashCode);
return new HashCode(newHashValue);
}
/// <summary>
/// Implicit cast operator to int.
/// </summary>
/// <param name="hashCode">Hash code to convert.</param>
public static implicit operator int(HashCode hashCode) => hashCode._value;
}
可以这样使用:
public override int GetHashCode() => new HashCode(Prop1) + Prop2;