class Employee
{
public string? Name { get; set; }
public int Age { get; set; }
}
// IComparer<Employee> is used to compare 2 Employee objects
// IComparer<string> is used to compare strings
// Since, you have to compare names of 2 employees
// hence, IComparer<Employee> is valid
class EmployeeNameComparer : IComparer<Employee>
{
public int Compare(Employee? x, Employee? y)
{
if (x != null && y != null)
{
return string.Compare(x.Name, y.Name, StringComparison.Ordinal);
}
else
{
return 0;
}
}
}
class Program
{
static void Main(string[] args)
{
Employee[] employees = {
new Employee { Name = "Rajesh", Age = 21 } ,
new Employee { Name = "Ajay", Age = 23 },
new Employee { Name="Niraj",Age=31}
};
Console.WriteLine("---Before Sorting---");
foreach (var employee in employees)
{
Console.WriteLine($"Name: {employee.Name} Age:{employee.Age}");
}
Array.Sort<Employee>(employees, new EmployeeNameComparer());
Console.WriteLine("---After Sorting---");
foreach (var employee in employees)
{
Console.WriteLine($"Name: {employee.Name} Age:{employee.Age}");
}
}
}OUTPUT
---Before Sorting---
Name: Rajesh Age:21
Name: Ajay Age:23
Name: Niraj Age:31
---After Sorting---
Name: Ajay Age:23
Name: Niraj Age:31
Name: Rajesh Age:21The nongeneric IComparer example is given in another post. Click here.
The generic IComparer<in T> and Contravariance
Since the generic IComparer<T> supports contravariance, we can use more derived type also. For example, IComparer<Employee> can be substituted by IComparer<Manager> where Manager is derived class from Employee class.
// This compiles successfully ONLY because of contravariance (the 'in' keyword)
IComparer<Manager> managerComparer = new EmployeeNameComparer();
Since IComparer<in T> by definition supports contravariance. It means if I have IComparer<Employee> implemented by EmployeeNameComparer then IComparer<Manager> need not be again implemented by some other class. Same class EmployeeNameComparer can be used to sort managers name.
Example.
class Employee
{
public string? Name { get; set; }
public int Age { get; set; }
}
class Manager : Employee
{
public int Salary { get; set; }
}
// IComparer<Employee> is used to compare 2 Employee objects
// IComparer<string> is used to compare strings
// Since, you have to compare names of 2 employees
// hence, IComparer<Employee> is valid
class EmployeeNameComparer : IComparer<Employee>
{
public int Compare(Employee? x, Employee? y)
{
if (x != null && y != null)
{
return string.Compare(x.Name, y.Name, StringComparison.Ordinal);
}
else
{
return 0;
}
}
}
class Program
{
static void Main(string[] args)
{
Manager[] managers = {
new Manager{ Name = "Rajesh", Age = 21 } ,
new Manager{ Name = "Ajay", Age = 23 },
new Manager{ Name="Niraj",Age=31}
};
Console.WriteLine("---Before Sorting---");
foreach (var manager in managers)
{
Console.WriteLine($"Name: {manager.Name} Age:{manager.Age}");
}
Array.Sort<Employee>(managers, new EmployeeNameComparer());
Console.WriteLine("---After Sorting---");
foreach (var manager in managers)
{
Console.WriteLine($"Name: {manager.Name} Age:{manager.Age}");
}
}
}
Point to Note.In C#, arrays of reference types are covariant. This means that a Manager[] can be implicitly cast to an Employee[].
When you call:
Array.Sort(managers, new EmployeeNameComparer());
The C# compiler does not see a type mismatch because it implicitly cast Manager[] array into an Employee[]. It treats the array as a collection of employees, sorts them using Employee comparer, and because they are still Manager objects under the hood, it works flawlessly at runtime!
No comments:
Post a Comment