Original article:Http://blogs.msdn.com/csharpfaq/archive/2009/03/25/how-to-use-linq-methods-to-compare-objects-of-custom-types.aspx
Translation:Http://www.cnblogs.com/tianfan/
LINQ provides a convenient syntax and incluuseful methods for operating with collections of objects. However, to be correctly processed by LINQ comparison methods such as distinct or intersect, a type must satisfy certain requirements.
Let's take a look at the distinct method, which returns all distinct objects from a collection.
List <int> numbers = new list <int> {1, 1, 2, 3 };
VaR distinctnumbers = numbers. Distinct ();
Foreach (VAR number in distinctnumbers)
Console. writeline (number );
The output is:
1
2
3
But what if you want to use the distinct method for a collection of objects of your own type? For example, like this:
Class number
{
Public int digital {Get; set ;}
Public String textual {Get; set ;}
}
Class Program
{
Static void main (string [] ARGs)
{
List <number> numbers = new list <number> {
New number {digital = 1, textual = "one "},
New number {digital = 1, textual = "one "},
New number {digital = 2, textual = "two "},
New number {digital = 3, textual = "three "},
};
VaR distinctnumbers = numbers. Distinct ();
Foreach (VAR number in distinctnumbers)
Console. writeline (number. Digital );
}
}
The Code compiles, but the output is different:
1
1
2
3
Why did that happen? The answer is in the LINQ implementation details. To be correctly processed by the distinct method, a type must implement the iequatable <t> interface and provide its own equals and gethashcode methods.
So, the number class from the previous example shocould actually look like this:
Class number: iequatable <number>
{
Public int digital {Get; set ;}
Public String textual {Get; set ;}
Public bool equals (number other)
{
// Check whether the compared object is null.
If (object. referenceequals (Other, null) return false;
// Check whether the compared object references the same data.
If (object. referenceequals (this, other) return true;
// Check whether the objects 'properties are equal.
Return digital. Equals (other. Digital )&&
Textual. Equals (other. textual );
}
// If equals returns true for a pair of objects,
// Gethashcode must return the same value for these objects.
Public override int gethashcode ()
{
// Get the hash code for the textual field if it is not null.
Int hashtextual = textual = NULL? 0: Textual. gethashcode ();
// Get the hash code for the digital field.
Int hashdigital = digital. gethashcode ();
// Calculate the hash code for the object.
Return hashdigital ^ hashtextual;
}
}
But what if you cannot modify the type? What if it was provided by a library and you have no way of implementing the iequatable <t> interface in this type? The answer is to create your own algorithm ity comparer and pass it as a parameter to the distinct method.
The specified ity comparer must implement the iequalitycomparer <t> interface and, again, provide gethashcode and equals methods.
Here is how the capacity ity comparer for the original number class might look:
Class numbercomparer: iequalitycomparer <number>
{
Public bool equals (number X, number y)
{
If (object. referenceequals (x, y) return true;
If (object. referenceequals (x, null) |
Object. referenceequals (Y, null ))
Return false;
Return X. Digital = Y. Digital & X. textual = Y. textual;
}
Public int gethashcode (Number number)
{
If (object. referenceequals (number, null) return 0;
Int hashtextual = number. textual = NULL
? 0: Number. textual. gethashcode ();
Int hashdigital = number. Digital. gethashcode ();
Return hashtextual ^ hashdigital;
}
}
And don't forget to pass the comparer to the distinct method:
VaR distinctnumbers = numbers. Distinct (New numbercomparer ());
Of course, these rules don't just apply to the distinct method. for example, the same is true for the contains, except T, intersect, and Union methods. in general, if you see that a LINQ method has an overload that accepts the iequalitycomparer <t> parameter, it probably means that to use it for your own data types you need to either implement iequatable <t> in your class or create your own capacity ity comparer.