2016-01-28 3 views
1

У меня есть этот объект:переопределение GetHashCode с различными свойствами

public class Foo { 
    public string MyOwnId { get; set; } 
    public Guid FooGuid { get; } = Guid.NewGuid(); 
} 

Я хотел бы Equals() только заботиться о тех, с MyOwnId, в противном случае они не никогда равны. Когда у Foo есть MyOwnId, я пытаюсь его использовать, в противном случае я хочу использовать FooGuid.

С FooGuid вероятно, никогда не будет таким же, я сделал что-то вроде этого:

public bool Equals(Foo foo) { 
     if (foo== null) return false; 
     return MyOwnId.Equals(foo.MyOwnId); 
    } 

    public override bool Equals(object obj) { 
     if (ReferenceEquals(null, obj)) return false; 
     if (ReferenceEquals(this, obj)) return true; 
     if (obj.GetType() != this.GetType()) return false; 
     return Equals((Foo)obj); 
    } 

    public override int GetHashCode() { 
     int hash = 13; 
     hash = (hash*7) + (!string.IsNullOrEmpty(MyOwnId) ? MyOwnId.GetHashCode() : FooGuid.GetHashCode()); 
     return hash; 
    } 

Является ли это правильный способ делать то, что я хочу? Или мне также нужно изменить свой метод Equals, чтобы он выглядел так же, как у моего GetHashCode? Для например:

public bool Equals(Foo foo) { 
     if (foo == null) return false; 
     if (string.IsNullOrEmpty(MyOwnId) || string.IsNullOrEmpty(foo.MyOwnId)) return false; 
     return MyOwnId.Equals(foo.MyOwnId); 
    } 
+0

Если 'MyOwnId == null', то' return MyOwnId.Equals (foo.MyOwnId); 'будет выдавать * исключение *. Измените его на 'String.Equals (MyOwnId, foo.MyOwnId)' –

+0

@DmitryBychenko Хорошо, но как насчет моего другого примера Equals? Это касается нулевых значений. – MrProgram

+0

Если два экземпляра (например, A и B) равны * в соответствии с 'Equals', они должны * иметь * тот же * хэш-код. Однако 'GetHashCode()' зависит от 'FooGuid' и' Equals' * не *, поэтому правило * нарушено *. –

ответ

2

Ну, давайте посмотрим. Ваша реализация Equals и GetHashCodeошибочна.

Оба Equals и GetHashCode не должны исключать исключение; счетчик пример

Foo A = new Foo(); 
    Foo B = new Foo() { 
    MyOwnId = "bla-bla-bla", 
    }; 

    // Throws an exception 
    if (A.Equals(B)) {} 

Если два экземпляра равны с помощью Equals эти экземпляры должны иметь одинаковый хеш-код; счетчик пример

Foo A = new Foo() { 
    MyOwnId = "", 
    }; 

    Foo B = new Foo() { 
    MyOwnId = "", 
    }; 

    if (A.Equals(B)) { 
    // Hashcodes must be equal and they are not 
    Console.Write(String.Format("{0} != {1}", A.GetHashCode(), B.GetHashCode())); 
    } 

Возможных (простейшие) реализация

// since you've declared Equals(Foo other) let others know via interface implementation 
public class Foo: IEquatable<Foo> { 
    public string MyOwnId { get; set; } 
    public Guid FooGuid { get; } = Guid.NewGuid(); 

    public bool Equals(Foo other) { 
    if (Object.ReferenceEquals(this, other)) 
     return true; 
    else if (Object.ReferenceEquals(null, other)) 
     return false; 
    else 
     return String.Equals(MyOwnId, other.MyOwnId); 
    } 

    public override bool Equals(object obj) { 
    return Equals(obj as Foo); // do not repeat youself: you've got Equals already 
    } 

    public override int GetHashCode() { 
    // String.GetHashCode is good enough, do not re-invent a wheel 
    return null == MyOwnId ? 0 : MyOwnId.GetHashCode(); 
    } 
} 
0

Хэш-функция должна обладать следующими свойствами:

  • Если два объекта сравниваются как равные, метод GetHashCode для каждого объекта должен возвращать то же значение. Однако, если два объекта не сравниваются как равные, методы GetHashCode для двух объектов не должны возвращать разные значения.
  • Метод GetHashCode для объекта должен последовательно возвращать один и тот же хэш-код, если не существует модификации состояния объекта, которая определяет возвращаемое значение метода Equals объекта. Обратите внимание, что это верно только для текущего выполнения приложения и что другой хеш-код может быть возвращен, если приложение запускается снова.
  • Для обеспечения максимальной производительности хеш-функция должна генерировать равномерное распределение для всех входных данных, включая ввод, который сильно кластеризуется. Подразумевается, что небольшие изменения состояния объекта должны приводить к большим изменениям в результате хеш-кода для лучшей производительности хеш-таблицы.
  • Функции хеширования должны быть недорогими для вычисления.
  • Метод GetHashCode не должен генерировать исключения.

См https://msdn.microsoft.com/en-us/library/system.object.gethashcode(v=vs.110).aspx

1

Или мне нужно изменить мой метод Equals так она выглядит так же, как мой GetHashCode?

Вы меняете свой Equals, чтобы соответствовать разрешению равенства. Вы это сделали.

Вы меняете свой GetHashCode() ключом на ту же информацию. В этом случае:

public override int GetHashCode() 
{ 
    return MyOwnId == null ? 0 : MyOwnId.GetHashCode(); 
} 

Кстати, ваш Equals(object) немного чрезмерно сложным. Я хотел бы использовать:

public override bool Equals(object obj) 
{ 
    return Equals(obj as Foo); 
} 

Это проходит обработку в случае obj быть нулевым для конкретного Equals() (который должен справиться с этим тоже), имеет дело с obj нечто, что не является Foo, передав, что Equals() Нуль (так или иначе, ложно) и передает обработку дела obj, являющегося чем-то, полученным от Foo, к более конкретным (что опять же необходимо обработать).

Сокращение по ReferenceEquals не стоит здесь делать, поскольку сравнивается только одно поле, и его сравнение будет иметь те же ReferenceEquals ярлык. Вы не используете ручку foo, являющуюся производным типом в специализированном Foo. Если Foo не запечатан, вы должны включать в себя следующее:

public bool Equals(Foo foo) 
{ 
    return (object)foo != null && 
    foo.GetType() == GetType() && 
    MyOwnId.Equals(foo.MyOwnId); 
} 

Если Foo запечатана то, что GetType() сравнение должно быть опущено.

Если логика Equals() была более сложной, чем это, то любит:

public bool Equals(Foo foo) 
{ 
    if ((object)foo == (object)this) 
    return true; 
    return (object)foo != null && 
    foo.GetType() == GetType() && 
    // Some more complicated logic here. 
} 

бы действительно быть полезным, но опять-таки это должно быть в определенной перегрузки не общее переопределения.

(Выполнение проверки ссылочного равенства более полезно в переполнениях ==, поскольку они должны учитывать возможность того, что оба операнда являются нулевыми, поэтому они могут также рассмотреть вопрос о том, что из них оба являются одинаковыми, что подразумевает этот случай) ,

Смежные вопросы