Использование поля объекта в качестве общего ключа словаря - PullRequest
111 голосов
/ 11 марта 2009

Если я хочу использовать объекты в качестве ключей для Dictionary, какие методы мне нужно будет переопределить, чтобы они сравнивались определенным образом?

Скажем, у меня есть класс, который имеет свойства:

class Foo {
    public string Name { get; set; }
    public int FooID { get; set; }

    // elided
} 

И я хочу создать:

Dictionary<Foo, List<Stuff>>

Я хочу, чтобы Foo объекты с одинаковым FooID считались одной и той же группой. Какие методы мне нужно переопределить в классе Foo?

Подводя итог: я хочу классифицировать Stuff объекты в списки, сгруппированные по Foo объектам. У Stuff объектов будет FooID, чтобы связать их с их категорией.

Ответы [ 5 ]

140 голосов
/ 11 марта 2009

По умолчанию двумя важными методами являются GetHashCode() и Equals(). Важно, что если две вещи равны (Equals() возвращает true), они имеют одинаковый хэш-код. Например, вы можете «вернуть FooID;» как GetHashCode(), если вы хотите, чтобы это соответствовало. Вы также можете реализовать IEquatable<Foo>, но это необязательно:

class Foo : IEquatable<Foo> {
    public string Name { get; set;}
    public int FooID {get; set;}

    public override int GetHashCode() {
        return FooID;
    }
    public override bool Equals(object obj) {
        return Equals(obj as Foo);
    }
    public bool Equals(Foo obj) {
        return obj != null && obj.FooID == this.FooID;
    }
}

Наконец, другая альтернатива - предоставить IEqualityComparer<T> для того же.

30 голосов
/ 11 марта 2009

Поскольку вы хотите, чтобы FooID был идентификатором для группы, вы должны использовать его в качестве ключа в словаре вместо объекта Foo:

Dictionary<int, List<Stuff>>

Если вы используете объект Foo в качестве ключа, вы просто реализуете метод GetHashCode и Equals, чтобы рассматривать только свойство FooID. Свойство Name было бы просто мертвым весом для Dictionary, так что вы просто использовали бы Foo в качестве оболочки для int.

Поэтому лучше использовать значение FooID напрямую, и тогда вам не нужно ничего реализовывать, поскольку Dictionary уже поддерживает использование int в качестве ключа.

Edit:
Если вы все равно хотите использовать класс Foo в качестве ключа, IEqualityComparer<Foo> легко реализовать:

public class FooEqualityComparer : IEqualityComparer<Foo> {
   public int GetHashCode(Foo foo) { return foo.FooID.GetHashCode(); }
   public bool Equals(Foo foo1, Foo foo2) { return foo1.FooID == foo2.FooID; }
}

Использование:

Dictionary<Foo, List<Stuff>> dict = new Dictionary<Foo, List<Stuff>>(new FooEqualityComparer());
8 голосов
/ 11 марта 2009

Для Foo вам нужно переопределить object.GetHashCode () и object.Equals ()

Словарь вызовет GetHashCode (), чтобы вычислить хэш-корзину для каждого значения, и равно, чтобы сравнить, идентичны ли два Foo.

Убедитесь, что вы вычислили хорошие хеш-коды (избегайте множества одинаковых объектов Foo с одинаковым хеш-кодом), но убедитесь, что два равных Foo имеют одинаковый хеш-код. Возможно, вы захотите начать с Equals-Method, а затем (в GetHashCode ()) записать хэш-код каждого члена, который вы сравниваете в Equals.

public class Foo { 
     public string A;
     public string B;

     override bool Equals(object other) {
          var otherFoo = other as Foo;
          if (otherFoo == null)
             return false;
          return A==otherFoo.A && B ==otherFoo.B;
     }

     override int GetHashCode() {
          return 17 * A.GetHashCode() + B.GetHashCode();
     }
}
0 голосов
/ 14 октября 2017

У меня была такая же проблема. Теперь я могу использовать любой объект, который пробовал в качестве ключа, из-за переопределения Equals и GetHashCode.

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

public class Equality<T>
{
    public int GetHashCode(T classInstance)
    {
        List<FieldInfo> fields = GetFields();

        unchecked
        {
            int hash = 17;

            foreach (FieldInfo field in fields)
            {
                hash = hash * 397 + field.GetValue(classInstance).GetHashCode();
            }
            return hash;
        }
    }

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

        return Equals(classInstance, (T)obj);
    }

    private bool Equals(T classInstance, T otherInstance)
    {
        List<FieldInfo> fields = GetFields();

        foreach (var field in fields)
        {
            if (!field.GetValue(classInstance).Equals(field.GetValue(otherInstance)))
            {
                return false;
            }
        }

        return true;
    }

    private List<FieldInfo> GetFields()
    {
        Type myType = typeof(T);

        List<FieldInfo> fields = myType.GetTypeInfo().DeclaredFields.ToList();
        return fields;
    }
}

Вот как это используется в классе:

public override bool Equals(object obj)
    {
        return new Equality<ClassName>().Equals(this, obj);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            return new Equality<ClassName>().GetHashCode(this);
        }
    }
0 голосов
/ 02 апреля 2016

А как насчет Hashtable класса!

Hashtable oMyDic = new Hashtable();
Object oAnyKeyObject = null;
Object oAnyValueObject = null;
oMyDic.Add(oAnyKeyObject, oAnyValueObject);
foreach (DictionaryEntry de in oMyDic)
{
   // Do your job
}

Таким образом, вы можете использовать любой объект (ваш объект класса) в качестве общего ключа словаря:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...