Background
For more information on set operations, you can refer link,
- var numList = new List<int>(){ 4,5 };
- var numbers = new List<int>() { 3,4 };
- var commonItems = numList.Intersect(numbers);
- foreach(var item in commonItems)
- {
- Console.WriteLine(item);
- }
- //Output : 4
Here is the result as expected, i.e. commonItems contains element 4 which is common to both the lists. But here we are comparing the values by internally using the default Equals method.
But while doing these operations for complex types or objects, the default implementation doesn’t give the generally expected result. If the current instance is a reference type, the Equals(Object) method tests for reference equality, and a call to the Equals(Object) method is equivalent to a call to the ReferenceEquals method. Reference equality means that the object variables that are compared refer to the same object.
- //Problem with object comparison
- var students = new List<student>();
- var girl = new Student() { Name = "Simran", StudentId = 4 };
- var sameGirl = new Student() { Name = "Simran", StudentId = 4 };
- students.Add(girl);
- Console.WriteLine("Default equality : {0}",students.Contains(sameGirl));
- //Output : False
- public class Employee
- {
- public int Id { get; set; }
- public string Name { get; set; }
- //Allows to override method with argument type as Object only
- public override bool Equals(Object obj)
- {
- if (obj == null)
- return false;
- var emp = (Employee)obj;
- return emp.Id == Id && emp.Name == Name;
- }
- //For hash based comparison
- public override int GetHashCode() => new { Id, Name }.GetHashCode();
- }
Here we modify the Equals method to compare the contents/properties(Id and Name) of Employee object. A thing to be noted here is that Equals method will always have argument type as Object only so it needs casting to the required type.
- //Equality using overridden methods - Equal and GetHashCode of Object
- var emp1 = new Employee() { Id = 1, Name = "John" };
- var emp2 = new Employee() { Id = 1, Name = "John" };
- Console.WriteLine(emp1.Equals(emp2)); //True
- var empList = new List<Employee>();
- empList.Add(emp1);
- Console.WriteLine(empList.Contains(emp2)); //True
- var employees = new HashSet<Employee>();
- employees.Add(emp2);
- Console.WriteLine(employees.Contains(emp1)); //True
By implementing IEquatable<T> interface
Object class, we can get the expected outcome. The class to be compared (Person in this case) implements this interface.- public class Person : IEquatable<Person>
- {
- public int Age { get; set; }
- public string Name { get; set; }
- //Flexibility to use argument type other than Object
- public bool Equals(Person otherPerson)
- {
- if (otherPerson!= null && otherPerson.Age == Age && otherPerson.Name == Name)
- {
- return true;
- }
- return false;
- }
- //GetHashCode() method of Object base class is implemented for hash based comparison
- public override int GetHashCode() => new { Age, Name }.GetHashCode();
- }
Here the Equals method does not have the restriction (as evident in overridden Equals method of Object class) that the type of argument should be Object. So we use the argument of type of the class we want to compare (Person in this case). Here it compares the object otherPerson with the current instance of the class.
Following is the code to test the equality using this technique,
- var person1 = new Person() { Age = 21, Name = "Alice" };
- var person2 = new Person() { Age = 21, Name = "Alice" };
- Console.WriteLine(person1.Equals(person2)); //True
By implementing the IEqualityComparer<T> interface
StudentComparer in the below example and not by the class (Student) itself which is being compared.- public class Student
- {
- public int StudentId { get; set; }
- public string Name { get; set; }
- }
- /// <summary>
- /// External Class which implements IEqualityComparer to compare equality of two objects type /// </summary> public class StudentComparer : IEqualityComparer<Student>
- {
- public bool Equals(Student x, Student y)
- {
- if(x != null && y != null)
- {
- if(x.StudentId == y.StudentId && x.Name == y.Name)
- {
- return true;
- }
- }
- return false;
- }
- public int GetHashCode(Student obj) => new { obj.StudentId, obj.Name }.GetHashCode();
- }
- var studentList = new List<Student>();
- var girl = new Student() { Name = "Simran", StudentId = 4 };
- var sameGirl = new Student() { Name = "Simran", StudentId = 4 };
- studentList.Add(girl);
- var stuList = new List<Student>();
- stuList.Add(sameGirl);
- var commonStudents = studentList.Intersect(stuList,new StudentComparer()).ToList();
- foreach(var student in commonStudents)
- {
- Console.WriteLine("Id: {0} , Name: {1}",student.StudentId,student.Name);
- }
- Console.WriteLine(studentList.Contains(sameGirl, new StudentComparer())); // True
Abubakr MahdiPosted Aug 8, 2019, 3:47 PM
Nice one...
Amit MohantyPosted Jul 23, 2019, 11:17 PM
Nice blog..