Что должен вернуть GetHashCode, если идентификатор объекта равен нулю?

Какое из следующего правильное/лучшее, учитывая, что свойство identity может быть нулевым.

public override int GetHashCode()
{
    if (ID == null) {
        return base.GetHashCode();
    }
    return ID.GetHashCode();
}

ИЛИ

public override int GetHashCode()
{
    if (ID != null) {
        return ID.GetHashCode();
    }
     return 0;
}

Обновление 1: Обновлен второй вариант.

Обновление 2: Ниже приведены реализации Equals:

public bool Equals(IContract other)
{
    if (other == null)
        return false;
    if (this.ID.Equals(other.ID)) {
        return true;
    }
    return false;
}

public override bool Equals(object obj)
{
    if (obj == null)
        return base.Equals(obj);
    if (!obj is IContract) {
        throw new InvalidCastException("The 'obj' argument is not an IContract object.");
    } else {
        return Equals((IContract)obj);
    }
}

И идентификатор имеет тип string.

Ответы

Ответ 1

Это действительно зависит от того, что вы хотите добиться равенства - важно то, что два равных объекта возвращают один и тот же хэш-код. Что означает равенство, когда идентификатор равен нулю? В настоящее время ваш метод Equals должен возвращать true, если свойства ID имеют одинаковое значение... но мы не знаем, что он делает, если ID равен null.

Если вы действительно хотите поведение первой версии, я лично использовал бы:

return ID == null ? base.GetHashCode() : ID.GetHashCode();

EDIT: на основе вашего метода Equals, похоже, вы можете создать свой метод GetHashCode:

return ID == null ? 0 : ID.GetHashCode();

Обратите внимание, что ваш метод Equals(IContract other) также может выглядеть так:

return other != null && object.Equals(this.ID, other.ID);

Ваша текущая реализация фактически вызовет исключение, если this.ID имеет значение null...

Кроме того, ваш метод Equals(object) неверен - вы не должны генерировать исключение, если вам передан недопустимый тип объекта, вы должны просто вернуть false... ditto, если obj равно null. Поэтому вы можете просто использовать:

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

Однако я обеспокоен равенством, основанным на интерфейсе. Обычно два класса разных типов не должны считаться равными, даже если у реализации есть те же интерфейсы.

Ответ 2

Вы можете просто return 0;, вам нужно вернуть тот же HashCode для одних и тех же значений, и 0 не будет часто возвращаться ID.GetHashCode(), поэтому такая функция Хэша может быть довольно хорошо для любых нужд. Поскольку вы не комбинируете какие-либо значения (например, ID и хэши имен), его довольно четкий идентификатор является определяющим источником HashCode, поэтому фиксированный 0 для Null ID звучит разумно.

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

После ваших изменений я могу сказать, что во втором эквиваленте Equals слишком много кода, просто замените его на

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

Ваш Equals (IContract contract) переопределяет ошибку для меня, потому что единственное, что определяет контракт, - это ID, и если у IContract больше полей, чем у ID, это будет плохое переопределение символов.

PS: На самом деле, если IContract - это интерфейс, вам, вероятно, нужно заменить ваш IEquatable<IContract> на конкретный контракт IEquatable<ClassName>, потому что он будет плохим дизайном, чтобы иметь возможность вернуться к тому, что разные объекты класса, реализующие один и тот же интерфейс, равны причине равенство по определению требует проверки того, что объекты имеют один и тот же тип на первом этапе проверки равенства (обычно в 99,9% случаев)

Ответ 3

Возможно, что-то вроде этого?

override int GetHashCode()
{
    if (ID != null)
        return ID.GetHashCode();

    return DBNull.Value.GetHashCode();
}

Важно, чтобы два объекта с нулевыми идентификаторами считались равными?