.NET Framework supplies few useful interfaces for comparing object. Two of them: IEqualityComparer and IComparer might sound bit confusing at first, so in this article I will explain the difference between them and show them in use.
IEqualityComparer
This interface supports the comparison of objects for equality, but what does it truly mean? To understand what equality of two objects is, let's create simple class, so we can compare it later:
public class Student
{
public string Name { get; set; }
public string School { get; set; }
public Student(string name, string school)
{
this.Name = name;
this.School = school;
}
public override string ToString()
{
return string.Format("{0} from {1}", this.Name, this.School);
}
}
Now let's create new class implementing IEqalityComparer interface. I will use generics because I want to avoid boxing and unboxing from object.
If you would like to read more about advantages of using generics, see
this MSDN page.
Before I start, I need to explain how I want to use my comparer. I will use it to select one student, from each school. We can imagine this as some sort of 'School conference' where we need to select one representative from each group.
public class StudentSchoolEqualityComparer : IEqualityComparer<Student>
{
#region IEqualityComparer<Student> Members
public bool Equals(Student x, Student y)
{
if (x.School.Equals(y.School))
{
return true;
}
else
{
return false;
}
}
public int GetHashCode(Student obj)
{
return base.GetHashCode();
}
#endregion
}
In the example above we can see the only criterion used to check equality of Student object is School property. It means all students even with different names will be considered as equal if their School property is same. We can test our code as follow:
List<Student> Students = new List<Student>();
Students.Add(new Student("Jon", "University of Southern California"));
Students.Add(new Student("Philip", "MIT"));
Students.Add(new Student("Mike", "MIT"));
Students.Add(new Student("Adam", "Stanford University"));
Students.Add(new Student("George", "University of Southern California"));
Students.Add(new Student("Jessica", "MIT"));
Students.Add(new Student("Peter", "University of Southern California"));
Students.Add(new Student("Monica", "Stanford University"));
Console.WriteLine("All conference students: ");
foreach (Student s in Students)
{
Console.WriteLine(s);
}
List<Student> Representatives = new List<Student>();
StudentSchoolEqualityComparer comparer = new StudentSchoolEqualityComparer();
Representatives = Students.Distinct(comparer).ToList();
Console.WriteLine("\nRepresentatives: ");
foreach (Student s in Representatives)
{
Console.WriteLine(s);
}
Output:
All conference students:
Jon from University of Southern California
Philip from MIT
Mike from MIT
Adam from Stanford University
George from University of Southern California
Jessica from MIT
Peter from University of Southern California
Monica from Stanford University
Representatives:
Jon from University of Southern California
Mike from MIT
Adam from Stanford University
As we can see, even though every Student object is different, our StudentSchoolEqualityComparer class decided to select only three 'unique' entries. Using custom comparer we can easily determine the way when objects are considered as same.
What we should remember about IEqualityComparer interface, is we should use it every time when we want to perform equality comparisons.
IComparer
This interface is slightly different - it not only exposes different methods when implemented, but its main function is to support
sorting.
This time our task is to sort students based on the name of their school:
public class SchoolNameComparer : IComparer<Student>
{
#region IComparer<Student> Members
public int Compare(Student x, Student y)
{
if (x.School.Equals(y.School))
{
return 0;
}
else
{
return x.School.CompareTo(y.School);
}
}
#endregion
}
Note: because we decided to compare Student objects based on the string (School), we could also use built-in class CaseInsensitiveComparer:
public int Compare(Student x, Student y)
{
return (new CaseInsensitiveComparer()).Compare(x.School, y.School);
}
Now when our Comparer is ready we can use it to sort List<Student> Students:
Console.WriteLine("Original order of the list: ");
foreach (Student s in Students)
{
Console.WriteLine(s);
}
Console.WriteLine("\nSorted using SchoolNameComparer comparer: ");
SchoolNameComparer schoolComparer = new SchoolNameComparer();
Students.Sort(schoolComparer);
foreach (Student s in Students)
{
Console.WriteLine(s);
}
Output:
Original order of the list:
Jon from University of Southern California
Philip from MIT
Mike from MIT
Adam from Stanford University
George from University of Southern California
Jessica from MIT
Peter from University of Southern California
Monica from Stanford University
Sorted using SchoolNameComparer comparer:
Mike from MIT
Philip from MIT
Jessica from MIT
Monica from Stanford University
Adam from Stanford University
Jon from University of Southern California
Peter from University of Southern California
George from University of Southern California
As you can see Student objects are correctly sorted based on the value of School property.
Both interfaces are extremely useful when we have to deal with lists and arrays. They allow us to perform sort and select operations based on any criteria we want. Custom EqalityComparers and Comparers can save a lot of time when we must often select and sort our custom data.
Post new comment