В чем разница между использованием IEqualityComparer и equals/GetHashCode Override?

когда я использую словари, иногда мне нужно изменить значение по умолчанию равно значению, чтобы сравнить ключи. Я вижу, что если я переопределяю Equals и GetHashCode в классе ключа или создаю новый класс, который реализует IEqualityComparer, у меня тот же результат. Итак, в чем разница между использованием IEqualityComparer и equals/GetHashCode Override? Два Примера:

class Customer
{
    public string name;
    public int age;
    public Customer(string n, int a)
    {
        this.age = a;
        this.name = n;
    }
    public override bool Equals(object obj)
    {
        Customer c = (Customer)obj;
        return this.name == c.name && this.age == c.age;
    }
    public override int GetHashCode()
    {
        return (this.name + ";" + this.age).GetHashCode();
    }
}
  class Program
{
    static void Main(string[] args)
    {
        Customer c1 = new Customer("MArk", 21);
        Customer c2 = new Customer("MArk", 21);
        Dictionary<Customer, string> d = new Dictionary<Customer, string>();
        Console.WriteLine(c1.Equals(c2));
        try
        {
            d.Add(c1, "Joe");
            d.Add(c2, "hil");
            foreach (KeyValuePair<Customer, string> k in d)
            {
                Console.WriteLine(k.Key.name + " ; " + k.Value);
            }
        }
        catch (ArgumentException)
        {
            Console.WriteLine("Chiave già inserita in precedenza");
        }
        finally
        {
            Console.ReadLine();
        }
    }
}

}

второй :

class Customer
{
    public string name;
    public int age;
    public Customer(string n, int a)
    {
        this.age = a;
        this.name = n;
    }
}
class DicEqualityComparer : EqualityComparer<Customer>
{
    public override bool Equals(Customer x, Customer y) // equals dell'equalitycomparer
    {
        return x.name == y.name && x.age == y.age;
    }
    public override int GetHashCode(Customer obj)
    {
        return (obj.name + ";" + obj.age).GetHashCode();
    }
}
class Program
{
    static void Main(string[] args)
    {
        Customer c1 = new Customer("MArk", 21);
        Customer c2 = new Customer("MArk", 21);
        DicEqualityComparer dic = new DicEqualityComparer();
        Dictionary<Customer, string> d = new Dictionary<Customer, string>(dic);
        Console.WriteLine(c1.Equals(c2));
        try
        {
            d.Add(c1, "Joe");
            d.Add(c2, "hil");
            foreach (KeyValuePair<Customer, string> k in d)
            {
                Console.WriteLine(k.Key.name + " ; " + k.Value);
            }
        }
        catch (ArgumentException)
        {
            Console.WriteLine("Chiave già inserita in precedenza");
        }
        finally
        {
            Console.ReadLine();
        }
    }
}

}

оба примера результат тот же.

спасибо заранее.

4 ответов


при переопределении Equals и GetHashCode вы меняете способ, которым объект будет определять, равен ли он другому. И примечание, если вы сравниваете объекты с помощью == оператор не будет иметь такое же поведение как Equals Если вы не измените оператора.

делая это, вы изменили поведение для одного класса, что делать, если вам нужна та же логика для других классов? Если вам нужно "общее сравнение". Вот почему у вас IEqualityComparer.

посмотреть пример:

interface ICustom
{
    int Key { get; set; }
}
class Custom : ICustom
{
    public int Key { get; set; }
    public int Value { get; set; }
}
class Another : ICustom
{
    public int Key { get; set; }
}

class DicEqualityComparer : IEqualityComparer<ICustom>
{
    public bool Equals(ICustom x, ICustom y)
    {
        return x.Key == y.Key;
    }

    public int GetHashCode(ICustom obj)
    {
        return obj.Key;
    }
}

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

var a = new Custom { Key = 1, Value = 2 };
var b = new Custom { Key = 1, Value = 2 };
var c = new Custom { Key = 2, Value = 2 };
var another = new Another { Key = 2 };

var d = new Dictionary<ICustom, string>(new DicEqualityComparer());

d.Add(a, "X");
// d.Add(b, "X"); // same key exception
d.Add(c, "X");
// d.Add(another, "X"); // same key exception

обратите внимание, что мне не пришлось переопределять Equals, GetHashCode ни в одном из классов. Я могу использовать этот компаратор в любом объекте, который реализует ICustom без необходимости переписывать логику сравнения. Я также могу сделать IEqualityComparer для "родительского класса" и использовать для классов, которые наследуют. У меня может быть компаратор, который будет вести себя по-другому, я могу сделать его для сравнения Value вместо Key.

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


объекта Equals() и GetHashCode() реализовать концепцию равенства, присущую объекту. Однако вы можете использовать альтернативные концепции равенства - например, компаратор равенства для объектов адреса, который использует только почтовый индекс, а не полный адрес.


Это по существу то же самое для этой цели с одной тонкой разницей. В первом примере вы переопределяете Equals, используя параметр типа Object, а затем должны передать его клиенту, однако во втором примере вы можете иметь параметр типа Customer, что означает, что нет необходимости в приведении.

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


есть много случаев, когда можно было бы иметь Dictionary найдите объекты, используя что-то отличное от 100% эквивалентности. В качестве простого примера можно пожелать иметь словарь, который соответствует без учета регистра. Один из способов сделать это-преобразовать строки в каноническую форму верхнего регистра, прежде чем хранить их в словаре или выполнять поиск. Альтернативным подходом является предоставление словарю IEqualityComparer<string> который будет вычислять хэш-коды и проверять равенство в какой-то независимой от случая функции. Есть некоторые обстоятельства, когда преобразование строк в каноническую форму и использование этой формы, когда это возможно, будет более эффективным, но есть и другие, где более эффективно хранить строку только в ее исходной форме. Одна из функций, которую я хотел бы иметь .NET, которая улучшила бы полезность таких словарей, была бы средством запроса фактического ключевого объекта, связанного с данным ключом (поэтому, если словарь содержит строку "WowZo" в качестве ключа можно посмотреть "wowzo" и вам "WowZo"; к сожалению, единственный способ получить фактический объект, если TValue не содержит избыточной ссылки на него, заключается в перечислении всей коллекции).

другой сценарий, где может быть полезно иметь альтернативные средства сравнения, когда объект содержит ссылку на экземпляр изменяемого типа, но никогда не будет подвергать этот экземпляр чему-либо, что может его мутировать. В общем, два экземпляра int[] что удержание одной и той же последовательности значений не будет взаимозаменяемым, поскольку в будущем одно или оба значения могут быть изменены для хранения разных значений. С другой стороны, если словарь будет использоваться для хранения и посмотри int[] значения, каждое из которых будет единственной ссылкой в любом месте Вселенной на экземпляр int[], и если ни один из экземпляров не будет изменен или подвергнут внешнему коду, может быть полезно рассматривать как равные экземпляры массива, которые содержат идентичные последовательности значений. С Array.Equals тесты на строгую эквивалентность (эталонное равенство), необходимо будет использовать некоторые другие средства тестирования массивов на эквивалентность.