Что делает RuntimeHelpers.GetHashCode

Метод RuntimeHelpers.GetHashCode(object) позволяет генерировать хэш-коды на основе идентичности объекта. MSDN состояния:

Метод RuntimeHelpers.GetHashCode всегда вызывает Object.GetHashCode не виртуально, даже если тип объекта имеет переопределил метод Object.GetHashCode.

[MethodImpl(MethodImplOptions.InternalCall)]
[SecuritySafeCritical]
public static extern int GetHashCode(object o);

Однако при проверке метода Object.GetHashCode() с использованием Reflector (.NET 4.0) мы увидим следующий код:

public virtual int GetHashCode()
{
    return RuntimeHelpers.GetHashCode(this);
}

Это заставляет меня полагать, что документация MSDN неверна, поскольку вызов Object.GetHashCode из RuntimeHelpers.GetHashCode(object) приведет к переполнению стека.

Итак, каково фактическое поведение RuntimeHelpers.GetHashCode(object) и как оно работает? Как он вычисляет хэш?

Ответ 1

Я думаю, что документация MSDN пытается описать поведение , а не реализацию. Ключевой момент: RuntimeTypeHelpers возвращает реализацию по умолчанию, которую вы получили бы, если бы object.GetHashCode() не был переопределен.

Это действительно полезно, если, например, вы хотите создать поиск ссылок по ссылке, даже для типов, которые переопределили Equals и GetHashCode. Я делаю это в сериализаторе, который поддерживаю, используя RuntimeTypeHelpers.GetHashCode() и Object.ReferenceEquals.

Ответ 2

Дело в том, что object.GetHashCode() можно переопределить - и часто это, например, на string. Это означает, что вы не можете узнать "хэш-код идентификации", который возвращает реализация object.GetHashCode() по умолчанию.

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

Например:

public class IdentityComparer<T> : IEqualityComparer<T> where T : class
{
    public bool Equals(T x, T y)
    {
        // Just for clarity...
        return object.ReferenceEquals(x, y);
    }

    public int GetHashCode(T x)
    {
        // The nullity check may be unnecessary due to a similar check in
        // RuntimeHelpers.GetHashCode, but it not documented
        return x == null ? 0 : RuntimeHelpers.GetHashCode(x);
    }
}

Тогда:

string x = "hello";
string y = new StringBuilder("h").Append("ello").ToString();
Console.WriteLine(x == y); // True (overloaded ==)
Console.WriteLine(x.GetHashCode() == y.GetHashCode()); // True (overridden)

IdentityComparer<string> comparer = new IdentityComparer<string>();
Console.WriteLine(comparer.Equals(x, y)); // False - not identity

// Very probably false; not absolutely guaranteed (as ever, collisions
// are possible)
Console.WriteLine(comparer.GetHashCode(x) == comparer.GetHashCode(y));

EDIT: просто немного уточнить...

Итак, каково фактическое поведение RuntimeHelpers.GetHashCode(объекта) и как оно работает?

Наблюдаемое поведение заключается в том, что значение, возвращаемое из RuntimeHelpers.GetHashCode(object), совпадает с значением, которое будет возвращено от не виртуального вызова до object.GetHashCode(). (Вы не можете записать, что не виртуальный вызов в С# легко.)

Что касается того, как это работает - это деталь реализации:) На самом деле не имеет значения IMO, что происходит вокруг (что называет). Важно то, что это правильно документированное поведение. Черт, разные версии mscorlib могли реализовать это по-другому - это не имело бы никакого значения с точки зрения пользователя. Без декомпиляции вы не должны понимать разницу.

Было бы (IMO) гораздо более запутанным для Object.GetHashCode для документирования в терминах RuntimeHelpers.GetHashCode().

Ответ 3

Странно, когда я смотрю на System.Object.GetHashCode через Reflector, я вижу

public virtual int GetHashCode()
{
    return InternalGetHashCode(this);
}

и для runtimehelper:

public static int GetHashCode(object o)
{
    return object.InternalGetHashCode(o);
}

Возможно, это рамочная разница? Я смотрю на сборки 2.0.

Ответ 4

Из вашего собственного вопроса, похоже, что RuntimeHelpers.GetHashCode(Object) действительно является реализацией неперекрываемого Object.GetHashCode().