Я ломал голову над неожиданным неудачным сравнением двух экземпляров пользовательской структуры.Я надеюсь, что кто-то может либо указать мне на источник или документацию, либо подтвердить, является ли это ошибкой!
При устранении проблемы получится что-то вроде следующего:
public readonly struct TwoDecimals
{
public readonly decimal First;
public readonly decimal Second;
public TwoDecimals(decimal first, decimal second)
{
this.First = first;
this.Second = second;
}
}
public void Main()
{
var withoutTrailingZero = new TwoDecimals(42m, 42m);
var withTrailingZero = new TwoDecimals(42.0m, 42m);
var equal = withoutTrailingZero.Equals(withTrailingZero); // true
var equalHashCodes = withoutTrailingZero.GetHashCode() == withTrailingZero.GetHashCode(); // false!
}
Это проблема,потому что если Equals
истинно, GetHashCode
определенно должно вернуть то же значение.
Я не переопределяю ни Equals
, ни GetHashCode
, поэтому пользовательская структура должна получить значение по умолчанию ValueType
реализация, которая может зависеть от платформы.Но читая в справочном источнике здесь Я вижу комментарий с этим утверждением:
Наш алгоритм возврата хеш-кода немного сложен.Мы ищем первое нестатическое поле и получаем его [sic] хэш-код.
Очевидно, что это не так, поскольку 42.0m
и 42m
имеют один и тот же хэш-код, несмотря на то, что имеют разные биты.
Кажется, что вместо этого он по какой-то причине возвращается к использованию битов.К сожалению, этот метод extern
, и я не уверен, как найти источник / a.
Источник ссылки имеет реализацию Equals
, которая использует отражение для сравнениякаждое поле структуры.Но сначала он делает эту проверку
// if there are no GC references in this object we can avoid reflection
// and do a fast memcmp
if (CanCompareBits(this))
return FastEqualsCheck(thisObj, obj);
FastEqualsCheck
, предположительно, просто сравнивает биты.В моем случае, это не должно использовать эту быструю проверку, потому что биты определенно отличаются, но я подозреваю, что что-то подобное может происходить в реализации для GetHashCode
.
Я также пробовал аналогичныеслучаи использования double
s (0.0d
и -0.0d
, которые имеют разные биты, но с одинаковым хеш-кодом и считаются равными) и float
s.Поведение все еще было не таким, как ожидалось, но по-другому!На этот раз хеш-коды снова были другими (и не такими, как предлагается в этом комментарии к коду, равными хеш-коду первого поля).Но на этот раз метод Equals
возвращает false
, поэтому, по крайней мере, он соответствует.