Могу ли я реализовать GetHashCode для своей пользовательской коллекции?

У меня есть пользовательская коллекция, как показано ниже

public class CustomCollection<T>:IEnumerable<T>, IEnumerator<T>
{
    int size = 0;
    int current = 0;
    int position = -1;
    CustomComparer<T> cmp = new CustomComparer<T>();

    T[] collection = null;
    public CustomCollection(int sizeofColl)
    {
        size = sizeofColl;
        collection = new T[size];
    }

    public void Push(T value)
    {
        if (!collection.Contains(value, cmp))
            collection[current++] = value;
    }

    public T Pop()
    {
        return collection[--current];
    }        

    IEnumerator<T> IEnumerable<T>.GetEnumerator()
    {
        return (IEnumerator<T>)this;
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        throw new NotImplementedException();
    }

    public T Current
    {
        get { return collection[position]; }
    }

    public void Dispose()
    {

    }

    object System.Collections.IEnumerator.Current
    {
        get { throw new NotImplementedException(); }
    }

    public bool MoveNext()
    {
        position++;
        if (position >= collection.Length)
            return false;
        else
            return true;
    }

    public void Reset()
    {
        throw new NotImplementedException();
    }
}

Теперь я хочу иметь коллекцию класса Person, как показано ниже, вместе с IEqualityComparer.

 public class Person
{
    public string Name { get; set; }
    public int ID { get; set; }       
}

public class CustomComparer<T>:IEqualityComparer<T>    {


    public bool Equals(T x, T y)
    {
        Person p1 = x as Person;
        Person p2 = y as Person;
        if (p1 == null || p2 == null)
            return false;
        else
            return p1.Name.Equals(p2.Name);
    }

    public int GetHashCode(T obj)
    {
        Person p = obj as Person;
        return p.Name.GetHashCode();
    }
}

Теперь, когда я выполняю следующую операцию над коллекцией, почему вызывается только метод Equals, а не GetHashCode()?

  CustomCollection.CustomCollection<Person> custColl = new CustomCollection<Person>(3);
        custColl.Push(new Person() { Name = "per1", ID = 1 });
        custColl.Push(new Person() { Name = "per2", ID = 2 });
        custColl.Push(new Person() { Name = "per1", ID = 1 });

Или как я могу заставить свой код вызывать GetHashCode?


person Vikram    schedule 07.03.2013    source источник
comment
Рекомендуемое чтение: stackoverflow.com/questions/371328/   -  person Richard    schedule 07.03.2013
comment
Кроме того, в вашей логике для вашего Equals(...) что произойдет, если оба имени пусты? Должны ли они считаться равными? Или должно быть выброшено исключение.   -  person Richard    schedule 07.03.2013
comment
@ Ричард, я думаю, было бы лучше бросить исключение   -  person Vikram    schedule 07.03.2013
comment
@Викрам Мех; лично я бы просто использовал return p1.Name == p2.Name - работа сделана и никаких проблем с null. Также; вы можете подумать, что произойдет, если и x, и y равны null. Большинство людей ожидает, что return true;   -  person Marc Gravell    schedule 07.03.2013
comment
Кстати, в .NET есть Stack<T> класс уже. И почему вы на самом деле хотите, чтобы GetHashCode называли?   -  person Groo    schedule 07.03.2013
comment
Если вы реализуете компаратор для класса Person, объявите его как PersonComparer : IEqualityComparer<Person>. То, что вы делаете сейчас, не имеет смысла (используя дженерики, а затем приведение к определенному классу).   -  person Groo    schedule 07.03.2013
comment
@Groo Большое спасибо за совет. Я хочу, чтобы вызывался GetHashCode, потому что, если я не ошибаюсь, это улучшает производительность, если коллекция большая.   -  person Vikram    schedule 07.03.2013
comment
@Vikram: не GetHashCode против Equals улучшает производительность. Он использует структуру данных (например, Dictionary), которая может использовать GetHashCode для сужения области поиска. В вашем случае вы используете Array.Contains, что требует временной сложности O(n) (он повторяет все элементы). Вы, вероятно, получите гораздо лучшее предложение, если напишите, что вы на самом деле пытаетесь сделать с этой структурой. Вы уверены, что эта структура должна работать как стек (LIFO)?   -  person Groo    schedule 07.03.2013


Ответы (1)


Это относится к строке:

if (!collection.Contains(value, cmp))

Проверка вектора или последовательности (поскольку это выглядит как Enumerable.Contains) не имеет смысла вызывать GetHashCode(); это полезно, если данные были сгруппированы в хэш-сегменты или какую-либо другую оптимизированную структуру, но здесь данные представляют собой просто плоскую последовательность значений. Если ему нужно вызвать метод, он может также вызвать Equals, а не GetHashCode(), потому что, если бы хеш был тем же самым, ему все равно нужно было бы вызвать Equals (хэш-код указывает на неравноправие, но не может указывать на равенство). Таким образом, можно выбрать вызов ровно одного метода для каждого объекта или не менее одного метода для каждого объекта и, возможно, двух методов для каждого объекта. Первый явно предпочтительнее.

Если бы данные были Dictionary<Person, ...> или HashSet<Person>, то я бы ожидал, что будет использоваться GetHashCode().

person Marc Gravell    schedule 07.03.2013
comment
В некоторых случаях для коллекций, не являющихся хеш-таблицами, может быть быстрее кэшировать хеш-значения их содержимого и использовать сравнение хэшей в качестве прелюдии к более подробному сравнению. Поиск значения в массиве целых чисел выполняется за O(1), точно так же, как поиск в списке значений того, который возвращает true для Equals, но может быть во много раз быстрее. - person supercat; 07.03.2013
comment
@supercat, который в основном зависит от наличия реализации для этого. Вектор/массив нет — данные здесь находятся в векторе. Таким образом, поиск значения в массиве целых чисел всегда будет O(n). Я согласен, что есть сценарии, в которых используется хеширование, но, поскольку код четко показывает, что мы используем вектор напрямую: это не один из них . - person Marc Gravell; 08.03.2013