使用IEqualityComparer和Equals/GethashCode Override的区别是什么?

本文关键字:区别 是什么 Override GethashCode IEqualityComparer Equals 使用 | 更新日期: 2023-09-27 18:18:34

当我使用字典时,有时我必须更改默认的Equals含义以便比较键。我看到,如果我在键的类上重写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();
        }
    }
}

}

两个示例的结果相同。

使用IEqualityComparer和Equals/GethashCode Override的区别是什么?

当您重写EqualsGetHashCode时,您正在改变对象确定它是否等于另一个对象的方式。注意,如果您使用==操作符比较对象,它将不会具有与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

注意,我不必在这两个类中重写EqualsGetHashCode。我可以在任何实现ICustom的对象中使用这个比较器,而不必重写比较逻辑。我还可以为"父类"制作IEqualityComparer,并在继承的类上使用。我可以让比较器以不同的方式运行,我可以让一个比较Value而不是Key

所以IEqualityComparer允许更大的灵活性,你可以实现通用的解决方案。

对象的Equals()GetHashCode()实现了对象固有的相等概念。但是,您可能希望使用其他相等概念—例如,地址对象的相等比较器只使用邮政编码而不是完整地址。

对于这个目的,本质上是相同的,只有一个细微的区别。在第一个示例中,您使用Object类型的参数重写Equals,然后必须将其强制转换为Customer,然而,在第二个示例中,您可以使用Customer类型的参数,这意味着不需要强制转换。

这意味着重写Equals允许在两个不同类型的对象之间进行比较(在某些情况下可能需要),然而,实现IEqualityComparer并没有提供这种自由(在某些情况下也可能需要)。

在许多情况下,人们可能希望Dictionary使用100%等价以外的东西来定位对象。作为一个简单的例子,人们可能希望有一个以不区分大小写的方式匹配的字典。实现这一目标的一种方法是,在将字符串存储在字典中或执行查找之前,将它们转换为规范的大写形式。另一种方法是为字典提供一个IEqualityComparer<string>,它将计算哈希码并在某种与大小写无关的函数中检查是否相等。在某些情况下,将字符串转换为规范形式并尽可能使用该形式会更有效,但在其他情况下,仅以原始形式存储字符串会更有效。我希望。net有一个功能可以提高字典的实用性,那就是请求与给定键相关联的实际键对象(因此,如果字典包含字符串"WowZo"作为键,则可以查找"wowzo"并获得"WowZo";不幸的是,如果TValue不包含对它的冗余引用,检索实际键对象的唯一方法是枚举整个集合)。

另一种情况是,当对象持有对可变类型实例的引用,但永远不会将该实例暴露给可能使其发生变异的任何东西时,使用另一种比较方法可能会有用。一般来说,保存相同值序列的int[]的两个实例不能互换,因为将来它们中的一个或两个可能被更改为保存不同的值。另一方面,如果将使用字典来保存和查找int[]值,其中每个值都将是对int[]实例的唯一引用,并且如果没有任何实例被修改或暴露给外部代码,则将其视为保存相同值序列的相等数组实例可能是有用的。由于Array.Equals测试严格等价(引用等价),因此有必要使用一些其他方法来测试数组的等价性。