24

当我使用字典时,有时我必须更改默认的 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

4 回答 4

18

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

于 2013-06-29T13:11:34.737 回答
4

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

于 2013-06-16T20:19:52.347 回答
1

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

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

于 2013-06-16T20:17:34.990 回答
1

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

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

于 2013-06-30T16:52:22.580 回答