Что необходимо переопределить в структуре, чтобы обеспечить правильное функционирование равенства?
Как видно из названия: мне нужно переопределить ==
оператор? как насчет .Equals()
метод? Что-то я пропускаю?
6 ответов
Пример из MSDN
public struct Complex
{
double re, im;
public override bool Equals(Object obj)
{
return obj is Complex && this == (Complex)obj;
}
public override int GetHashCode()
{
return re.GetHashCode() ^ im.GetHashCode();
}
public static bool operator ==(Complex x, Complex y)
{
return x.re == y.re && x.im == y.im;
}
public static bool operator !=(Complex x, Complex y)
{
return !(x == y);
}
}
Вы также должны реализовать IEquatable
Реализуйте IEquatable для типов значений. Метод Object.Equals для типов значений вызывает упаковку, и его реализация по умолчанию не очень эффективна, потому что использует рефекцию. IEquatable.Equals может предложить гораздо лучшую производительность и может быть реализован так, чтобы он не вызывал бокс.
public struct Int32 : IEquatable<Int32> {
public bool Equals(Int32 other){ ... }
}
Следуйте тем же правилам, что и для переопределения Object.Equals при реализации IEquatable.Equals. См. Раздел 8.7.1 для подробных рекомендаций по переопределению Object.Equals
К сожалению, у меня недостаточно репутации, чтобы комментировать другие записи. Поэтому я публикую здесь возможное улучшение топ-решения.
Поправьте меня, если я не прав, но реализация упомянута выше
public struct Complex
{
double re, im;
public override bool Equals(Object obj)
{
return obj is Complex && this == (Complex)obj;
}
public override int GetHashCode()
{
return re.GetHashCode() ^ im.GetHashCode();
}
public static bool operator ==(Complex x, Complex y)
{
return x.re == y.re && x.im == y.im;
}
public static bool operator !=(Complex x, Complex y)
{
return !(x == y);
}
}
Имеет большой недостаток. Я ссылаюсь на
public override int GetHashCode()
{
return re.GetHashCode() ^ im.GetHashCode();
}
XORing является симметричным, поэтому Complex(2,1) и Complex(1,2) дают одинаковый хэш-код.
Вероятно, мы должны сделать что-то вроде:
public override int GetHashCode()
{
return re.GetHashCode() * 17 ^ im.GetHashCode();
}
Большую часть времени вы можете избежать реализации Equals и GetHashcode в структурах - потому что компилятор автоматически реализует типы значений с использованием побитового содержимого + отражения для ссылочных членов.
Посмотрите на этот пост: что лучше всего подходит для хранилища данных Struct/Classes?
Так что для простоты использования вы все равно можете реализовать == и!=.
Но большую часть времени вы можете избежать реализации Equals и GetHashcode.
Случай, когда вам нужно реализовать Equals и GetHashCode, относится к полю, которое вы не хотите принимать во внимание.
Например, поле, которое меняется с течением времени, например, возраст человека или мгновенная скорость автомобиля (идентичность объекта не должна меняться, если вы хотите найти его в словаре в том же месте)
С уважением, лучший код
Просто для полноты я бы тоже советовал перегружать Equals
метод:
public bool Equals(Complex other)
{
return other.re == re && other.im == im;
}
это реальное улучшение скорости, так как нет входного аргумента входного аргумента Equals(Object obj)
метод
Некоторые рекомендации по использованию типов значений:
- сделать их неизменными
- переопределить Equals (тот, который принимает объект в качестве аргумента);
- перегрузка Равно для получения другого экземпляра того же типа значения (например, * Равно (Сложное другое));
- операторы перегрузки == и!=;
- переопределить GetHashCode
Это происходит из этого поста: http://theburningmonk.com/2015/07/beware-of-implicit-boxing-of-value-types/
Основное различие между ними состоит в том, что ==
оператор является статическим, т.е. соответствующий метод для вызова определяется во время компиляции, в то время как Equals
Метод вызывается динамически в экземпляре.
Определение обоих, вероятно, лучше всего сделать, даже если это имеет меньшее значение в случае структур, поскольку структуры не могут быть расширены (структура не может наследоваться от другой).