Рассмотрим следующий код:
struct Vec2 : IEquatable<Vec2>
{
double X,Y;
public bool Equals(Vec2 other)
{
return X.Equals(other.X) && Y.Equals(other.Y);
}
public override bool Equals(object obj)
{
if (obj is Vec2)
{
return Equals((Vec2)obj);
}
return false;
}
// this will return the same value when X, Y are swapped
public override int GetHashCode()
{
return X.GetHashCode() ^ Y.GetHashCode();
}
}
Помимо разговора о сравнении двойников на равенство (это просто демонстрационный код), меня беспокоит то, что при значениях X, Y возникает хэш-конфликтпоменялись местами.Например:
Vec2 A = new Vec2() { X=1, Y=5 };
Vec2 B = new Vec2() { X=5, Y=1 };
bool test1 = A.Equals(B); // returns false;
bool test2 = A.GetHashCode() == B.GetHashCode() // returns true !!!!!
, что должно разрушить коллекцию словарей.Таким образом, вопрос состоит в том, как задать свойство GetHashCode()
для 2,3 или даже 4 значений с плавающей запятой, чтобы результаты не были симметричными и хэши не конфликтовали.
Редактировать 1:
Point
реализует неуместное решение x ^ y
, а PointF
включает ValueType.GetHashCode()
.
Rectangle
имеет очень своеобразное (((X ^ ((Y << 13) | (Y >> 19))) ^ ((Width << 26) | (Width >> 6))) ^ ((Height << 7) | (Height >> 25)))
выражение для хеш-кода, которое, по-видимому, выполняет следующие функции:ожидается.
Редактировать 2:
System.Double имеет хорошую реализацию, поскольку он не считает каждый бит одинаково важным
public override unsafe int GetHashCode() //from System.Double
{
double num = this;
if (num == 0.0)
{
return 0;
}
long num2 = *((long*) &num);
return (((int) num2) ^ ((int) (num2 >> 32)));
}