Информация об изменениях

Сообщение Re: Правильный GetHashCode для сравнения byte[] по значению от 25.12.2016 9:45

Изменено 25.12.2016 9:57 hardcase

Здравствуйте, Shmj, Вы писали:

S>Т.к. GetHashCode возвращает int, то вовлечь достаточно 4 байта. Так же можно вовлечь длину массива (или 1 байт длины). Это будет и быстро и выполнять свои функции, то есть в большенстве случаев сработает. Согласны?


У меня есть утиллитка для сборки хэшей, использование очевидно:
    public struct HashCode : IEquatable<HashCode>
    {
        public readonly int Value;

        public HashCode(int value)
        {
            Value = value;
        }

        public HashCode(string text, StringComparer comparer)
        {
            Value = (object)text != null ? comparer.GetHashCode(text) : 0;
        }

        public override string ToString() =>
            "HashCode: " + Value;

        public override int GetHashCode() =>
            Value;

        public override bool Equals(object other) =>
            other is HashCode && Equals((HashCode)other);

        public bool Equals(HashCode other) =>
            Value == other.Value;

        public static bool operator ==(HashCode a, HashCode b) =>
            a.Value == b.Value;

        public static bool operator !=(HashCode a, HashCode b) =>
            a.Value != b.Value;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static implicit operator int(HashCode hashCode) =>
            hashCode.Value;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public HashCode Add(int newValue) =>
            new HashCode(unchecked((Value << 5) + Value ^ newValue));

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public HashCode Add(bool flag) =>
            Add(flag ? 1 : -1);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public HashCode Add<T>(T? obj)
            where T : struct =>
            obj.HasValue ? Add(obj.GetValueOrDefault().GetHashCode()) : this;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public HashCode Add<T>(T obj)
            where T : class =>
            obj != null ? Add(obj.GetHashCode()) : this;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public HashCode Add(string text, StringComparer comparer) =>
            (object)text != null ? Add(comparer.GetHashCode(text)) : this;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public HashCode AddGeneric<T>(T obj) =>
            Add(EqualityComparer<T>.Default.GetHashCode(obj));

        public HashCode AddArray<T>(T[] arr)
        {
            if (arr == null) { return this; }

            var count = arr.Length;
            var hashCode = Add(count);
            if (count > 0) { hashCode = hashCode.AddGeneric(arr[0]); }
            if (count > 1) { hashCode = hashCode.AddGeneric(arr[count - 1]); }
            return hashCode;
        }
...


Под профайлером эта радость гонялась, проблем пока не видел.
Здравствуйте, Shmj, Вы писали:

S>На SOF рекомендуют перебирать все элементы массива:


На SOF рекомендуют много всякой ерунды
На практике же перебор массива — слишком затратная операция чтобы при каждом обращении к GetHashCode ее выполнять. Хэши сложных объектов необходимо кэшировать, и использовать в реализации Equals.

S>Т.к. GetHashCode возвращает int, то вовлечь достаточно 4 байта. Так же можно вовлечь длину массива (или 1 байт длины). Это будет и быстро и выполнять свои функции, то есть в большенстве случаев сработает. Согласны?


У меня есть утиллитка для сборки хэшей, использование очевидно:
    public struct HashCode : IEquatable<HashCode>
    {
        public readonly int Value;

        public HashCode(int value)
        {
            Value = value;
        }

        public HashCode(string text, StringComparer comparer)
        {
            Value = (object)text != null ? comparer.GetHashCode(text) : 0;
        }

        public override string ToString() =>
            "HashCode: " + Value;

        public override int GetHashCode() =>
            Value;

        public override bool Equals(object other) =>
            other is HashCode && Equals((HashCode)other);

        public bool Equals(HashCode other) =>
            Value == other.Value;

        public static bool operator ==(HashCode a, HashCode b) =>
            a.Value == b.Value;

        public static bool operator !=(HashCode a, HashCode b) =>
            a.Value != b.Value;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static implicit operator int(HashCode hashCode) =>
            hashCode.Value;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public HashCode Add(int newValue) =>
            new HashCode(unchecked((Value << 5) + Value ^ newValue));

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public HashCode Add(bool flag) =>
            Add(flag ? 1 : -1);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public HashCode Add<T>(T? obj)
            where T : struct =>
            obj.HasValue ? Add(obj.GetValueOrDefault().GetHashCode()) : this;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public HashCode Add<T>(T obj)
            where T : class =>
            obj != null ? Add(obj.GetHashCode()) : this;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public HashCode Add(string text, StringComparer comparer) =>
            (object)text != null ? Add(comparer.GetHashCode(text)) : this;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public HashCode AddGeneric<T>(T obj) =>
            Add(EqualityComparer<T>.Default.GetHashCode(obj));

        public HashCode AddArray<T>(T[] arr)
        {
            if (arr == null) { return this; }

            var count = arr.Length;
            var hashCode = Add(count);
            if (count > 0) { hashCode = hashCode.AddGeneric(arr[0]); }
            if (count > 1) { hashCode = hashCode.AddGeneric(arr[count - 1]); }
            return hashCode;
        }
...


Под профайлером эта радость гонялась, проблем пока не видел.