When i am using dictionaries sometimes I have to change the default Equals meaning in order to compare Keys. I see that if I override the Equals and GetHashCode on the key's class or i create a new class which implements IEqualityComparer I have the same result. So what's the difference between using IEqualityComparer and Equals/GethashCode Override? Two Examples:
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();
}
}
}
}
Second one :
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();
}
}
}
}
Both examples have the same result.
Thanks in advance.
The object's
Equals()
anfGetHashCode()
implement the concept of equality intrinsic to the object. However, you might want to use alternative concepts of equality - for example, an equality comparer for address objects that only uses the ZIP code rather than the full address.It is essentially the same for this purpose with one subtle difference. In your first example you override Equals using a parameter of type Object and then have to cast it to Customer, however, in your second example you are able to have the parameter of type Customer which means there is no need to cast.
This means that overriding Equals allows comparison between two objects of different types (which may be needed in certain circumstances), however, implementing IEqualityComparer does not give this freedom (which may also be needed in certain circumstances).
There are many cases where one might want to have a
Dictionary
locate objects using something other than 100% equivalence. As a simple example, one may wish to have a dictionary which matches in case-insensitive fashion. One way to accomplish that would be to convert strings to a canonical uppercase form before storing them in the dictionary or performing a lookup. An alternative approach is to supply the dictionary with anIEqualityComparer<string>
which will compute hash-codes and check for equality in some sort of case-independent function. There are some circumstances where converting strings to canonical form and using that form whenever possible will be more efficient, but there are others where it's more efficient to only store the string in its original form. One feature I wish .NET had which would improve the usefulness of such dictionaries would be a means of requesting the actual key object associated with a given key (so if the dictionary contained the string"WowZo"
as a key, one could look up"wowzo"
and get"WowZo"
; unfortunately, the only way to retrieve an actual key object, ifTValue
doesn't contain a redundant reference to it, is to enumerate the entire collection).Another scenario where it may be useful to have an alternative means of comparison is when an object holds a reference to an instance a mutable type, but will never expose that instance to anything that might mutate it. In general, two instances of
int[]
that hold the same sequence of values will not be interchangeable, since it would be possible that in future one or both of them might be changed to hold different values. On the other hand, if a dictionary will be used to hold and look upint[]
values, each of which will be the only reference anywhere in the universe to an instance ofint[]
, and if none of the instances will be modified nor exposed to outside code, it may be useful to regard as equal array instances which hold identical sequences of values. SinceArray.Equals
tests for strict equivalence (reference equality), it would be necessary to use some other means of testing the arrays for equivalence.When you override
Equals
andGetHashCode
you are changing the way the object will determine if it is equals to another. And a note, if you compare objects using==
operator it will not have the same behavior asEquals
unless you override the operator as well.Doing that you changed the behavior for a single class, what if you need the same logic for other classes? If you need a "generic comparison". That is why you have
IEqualityComparer
.Look at this example:
I have two different classes, both can use the same comparer.
Notice that I didn't have to override
Equals
,GetHashCode
in neither of the classes. I can use this comparer in any object that implementsICustom
without having to rewrite the comparison logic. I can also make anIEqualityComparer
for a "parent class" and use on classes that inherit. I can have comparer that will behave in a different way, I can make one to compareValue
instead ofKey
.So
IEqualityComparer
allows more flexibility and you can implement generic solutions.