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

时间:2013-06-16 20:07:11

标签: c# equals equality gethashcode iequalitycomparer

当我使用词典时,有时我必须更改默认的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();
        }
    }
}

}

两个例子都有相同的结果。

提前致谢。

4 个答案:

答案 0 :(得分:12)

当您覆盖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可以提供更大的灵活性,您可以实现通用解决方案。

答案 1 :(得分:3)

对象的Equals() anf GetHashCode()实现对象固有的相等概念。但是,您可能希望使用等同的替代概念 - 例如,仅使用邮政编码而不是完整地址的地址对象的相等比较器。

答案 2 :(得分:1)

为此目的基本相同,只有一个微妙的区别。在第一个示例中,您使用Object类型的参数覆盖Equals,然后必须将其强制转换为Customer,但是,在第二个示例中,您可以使用Customer类型的参数,这意味着无需强制转换。

这意味着重写Equals允许在两个不同类型的对象之间进行比较(在某些情况下可能需要),但是,实现IEqualityComparer不会给予这种自由(在某些情况下也可能需要)。

答案 3 :(得分:1)

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

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