使用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();
}
}
}
}
两个示例的结果相同。
当您重写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()
实现了对象固有的相等概念。但是,您可能希望使用其他相等概念—例如,地址对象的相等比较器只使用邮政编码而不是完整地址。
对于这个目的,本质上是相同的,只有一个细微的区别。在第一个示例中,您使用Object类型的参数重写Equals,然后必须将其强制转换为Customer,然而,在第二个示例中,您可以使用Customer类型的参数,这意味着不需要强制转换。
这意味着重写Equals允许在两个不同类型的对象之间进行比较(在某些情况下可能需要),然而,实现IEqualityComparer并没有提供这种自由(在某些情况下也可能需要)。
在许多情况下,人们可能希望Dictionary
使用100%等价以外的东西来定位对象。作为一个简单的例子,人们可能希望有一个以不区分大小写的方式匹配的字典。实现这一目标的一种方法是,在将字符串存储在字典中或执行查找之前,将它们转换为规范的大写形式。另一种方法是为字典提供一个IEqualityComparer<string>
,它将计算哈希码并在某种与大小写无关的函数中检查是否相等。在某些情况下,将字符串转换为规范形式并尽可能使用该形式会更有效,但在其他情况下,仅以原始形式存储字符串会更有效。我希望。net有一个功能可以提高字典的实用性,那就是请求与给定键相关联的实际键对象(因此,如果字典包含字符串"WowZo"
作为键,则可以查找"wowzo"
并获得"WowZo"
;不幸的是,如果TValue
不包含对它的冗余引用,检索实际键对象的唯一方法是枚举整个集合)。
另一种情况是,当对象持有对可变类型实例的引用,但永远不会将该实例暴露给可能使其发生变异的任何东西时,使用另一种比较方法可能会有用。一般来说,保存相同值序列的int[]
的两个实例不能互换,因为将来它们中的一个或两个可能被更改为保存不同的值。另一方面,如果将使用字典来保存和查找int[]
值,其中每个值都将是对int[]
实例的唯一引用,并且如果没有任何实例被修改或暴露给外部代码,则将其视为保存相同值序列的相等数组实例可能是有用的。由于Array.Equals
测试严格等价(引用等价),因此有必要使用一些其他方法来测试数组的等价性。