web analytics

IComparable, IComparable<T>, IComparer and IComparer<T> in C#

Options

codeling 1595 - 6639
@2016-02-08 18:05:46

All four interfaces IComparable, IComparable<T>, IComparer and IComparer<T> provide comparision functionality to a C# class.

  • A class that implements IComparable or IComparable&lt<T> is comparable itself, it means that an object of that type can compare to another object directly.
  • A class that implements IComparer or IComparer<T> is a comparer, it means that an object of that type provides comparision functionality to a set of target objects which either have no comparison ability interally, or the comparison ability provides by themself is not one you want to.

In general, it is preferable to implement the generic interface System.IComparable<T> and System.IComparer<T> rather than the interface System.IComparable and System.IComparer. This is because the generic versions are much safer than the non-generic version by providing a generalized comparison method that a value type or class implements to create a type-specific comparison method for ordering instances.

Talking about whether a class should implements the IComparable or IComparable&lt<T> interface, or rather than creating a comparer class that implements the IComparer or IComparer<T>, it is determined by whether the the to-be-implemented comparision ability is intrinsic to the class or is only meaningful only in a specific context:

  • If the comparision ability is intrinsic to the class, then it is prefer to implement the the IComparable or IComparable<T> interface;
  • If the comparision ability is only meaningful only in a specific context, then it is prefer to creating a comparer class that implements the IComparer or IComparer<T> and provides comparision service to the target class.

The followng C# example dmonostrates how to apply the above guidlines to your code.

Employee Class

The following Employee class defines type of empolyee which has four field id, name, age and salary. it also implemnts IComparable<Employee> which makes object of this type is comparable based on its identity.

public class Employee : System.IComparable<Employee>
{
    private string _id;
    private string _name;
    private int _age;
    private double _salary;


    public string ID
    {
        get
        {
            return this._id;
        }
    }

    public string Name
    {
        get
        {
            return this._name;
        }
    }

    public int Age
    {
        get
        {
            return this._age;
        }
    }

    public double Salary
    {
        get
        {
            return this._salary;
        }
    }

    public Employee(string id, string name, int age, double salary)
    {
        this._id = id;
        this._name = name;
        this._age = age;
        this._salary = salary;
    }

    public override string ToString()
    {
        return string.Format("[ID: {0}, Name: {1}, Age: {2}, Salary: {3}]", this._id, this._name, this._age, this._salary);
    }

    #region IComparable<Employee> Members

    int IComparable<Employee>.CompareTo(Employee other)
    {
        return this.ID.CompareTo(other.ID);
    }

    #endregion
}

EmployeeNameComparer Class

The following EmployeeNameComparer class creates a comparer based on the name comparision. In case you want to compare two employees based on their name rather than their identity, this class is used for this purpose.

public class EmployeeNameComparer : IComparer<Employee>
{

    #region IComparer<Employee> Members

    int IComparer<Employee>.Compare(Employee x, Employee y)
    {
        return x.Name.CompareTo(y.Name);
    }

    #endregion
}

EmployeeAgeComparer Class

The following EmployeeAgeComparer class creates a comparer based on the age comparision. In case you want to compare two employees based on their age rather than their identity, this class is used for this purpose.

public class EmployeeAgeComparer : IComparer<Employee>
{

    #region IComparer<Employee> Members

    int IComparer<Employee>.Compare(Employee x, Employee y)
    {
        int rtn = 0;

        if (x.Age < y.Age)
        {
            rtn = -1;
        }
        else if (x.Age > y.Age)
        {
            rtn = 1;
        }

        return rtn;
    }

    #endregion
}

Main Program

The follwing main method demonostrates three different scenarios where use different comparision abilities.

class Program
{
        static void Main(string[] args)
        {
            List<Employee> employeeList = new List<Employee>();

            employeeList.Add(new Employee("100", "Derrel Brill", 22, 53000));
            employeeList.Add(new Employee("300", "Dave Gate", 30, 60000));
            employeeList.Add(new Employee("200", "Jimmy Renaud", 55, 80000));


            //Sorting argorithm is provided internally by Employee class

            Console.WriteLine("Sorted By ID");
            employeeList.Sort();
            foreach (Employee e in employeeList)
            {
                Console.WriteLine(e.ToString());
            }


            Console.WriteLine(Environment.NewLine);


            //Sorting argorithm is provided externally

            //by EmployeeNameComparer class
           
            Console.WriteLine("Sorting By Name");
            employeeList.Sort(new EmployeeNameComparer());

            foreach (Employee e in employeeList)
            {
                Console.WriteLine(e.ToString());
            }

            Console.WriteLine(Environment.NewLine);


            //Sorting argorithm is provided externally

            //by EmployeeAgeComparer class

            Console.WriteLine("Sorting By Age");
            employeeList.Sort(new EmployeeAgeComparer());

            foreach (Employee e in employeeList)
            {
                Console.WriteLine(e.ToString());
            }

            Console.ReadLine();
    }
}

@2016-02-09 08:15:37

When and How to Implement System.IComparable and System.IComparable<T> Interfaces in .NET

Because of the base class System.Object in the .NET Framework doesn't support comparision between two objects in this class, if you need to create a class (which is always inherited from System.Object) that requires you to be able to compare values according to some ordering, you should implement the System.IComparable and/or System.IComparable<T> interface. In general, it is preferable to implement the System.IComparable<T> interface rather than System.IComparable. You can also implement both, as many of the types in the .NET Framework do.

Implementing the System.IComparable Interface

The IComparable interface defines a generalized type-specific comparison method that a value type or class implements to order or sort its instances.

public interface IComparable
{
    int CompareTo(object obj);
}

This interface contains a method called CompareTo, which takes a single parameter specifying the object to be compared to the current instance and returns an integer that indicates the result of the comparison as shown in the following table.

Value

Meaning

Less than zero

The current instance is less than the value of the parameter

Zero

The current instance is equal to the value of the parameter.

Greater than zero

The current instance is greater than the value of the parameter

For example, you can make the following Circle class in C# comparable by implementing the System.IComparable interface and providing the CompareTo method. In the code below, the CompareTo method compares Circle objects based on their areas. The area of a circle with a larger area is greater than a circle with a smaller area.

public class Circle : System.IComparable
{
    //...
   
    public int CompareTo(object obj)
    {
        // cast the parameter to its real type
        Circle circObj = (Circle)obj; 
        if (this.Area() == circObj.Area())
            return 0;

        if (this.Area() > circObj.Area())
            return 1;

        return -1;
    }
   
    //...
}

If you examine the System.IComparable interface, you will see that its parameter is defined as an object. However, this approach is not typesafe. To understand why this is so, consider what happens if you try to pass something that is not a Circle to the CompareTo method. The System.IComparable interface requires the use of a cast in order to be able to access the Area method. If the parameter is not a Circle but some other type of object then this cast will fail.

Implementing the System.IComparable<T> Interface

Since .NET framework 2.0, a new generic interface named System.IComparable<T> is provided to support a generalized comparison method that a value type or class implements to create a type-specific comparison method for ordering instances.

public interface IComparable<T>
{
        int CompareTo(T other);

}

Also notice that these methods take a type parameter (T) rather than an object, and as such, are much safer than the non-generic version of the interface. The following code shows how you can implement this interface in the Circle class:

class Circle : System.IComparable<Circle>
{
    //...
    public int CompareTo(Circle other)
    {
        if (this.Area() == other.Area())
            return 0;

        if (this.Area() > other.Area())
            return 1;

        return -1;
    }

    //...
}

The parameters for the CompareTo must match the type specified in the interface, IComparable<Circle>.

As mentioned above, it is preferable to implement the System.IComparable<T> interface rather than System.IComparable. You can also implement both, as many of the types in the .NET Framework do.

@2016-02-11 08:02:45

List<T>.Sort() Method

The List<T>.Sort() method uses the default comparer Comparer<T>.Default for type T to determine the order of list elements. The Comparer<T>.Default property checks whether type T implements the IComparable<T> generic interface and uses that implementation, if available. If not, Comparer<T>.Default checks whether type T implements the IComparable interface. If type T does not implement either interface, Comparer<T>.Default throws an InvalidOperationException.

List<Employee> employeeList = new List<Employee>();
employeeList.Add(new Employee("100", "Derrel Brill", 22, 53000));
employeeList.Add(new Employee("300", "Dave Gate", 30, 60000));
employeeList.Add(new Employee("200", "Jimmy Renaud", 55, 80000));
//Sorting argorithm is provided internally by Employee class

 

Console.WriteLine("Sorted By ID");


employeeList.Sort();


foreach (Employee e in employeeList)
{
    Console.WriteLine(e.ToString());
}
@2016-02-12 08:13:21

List<T>.Sort(IComparer<T>) Method

The List<T>.Sort(IComparer<T>) method sorts the elements in the entire List<T> using the specified comparer.

If comparer is null, the default comparer Comparer<T>.Default checks whether type T implements the IComparable<T> generic interface and uses that implementation, if available. If not, Comparer<T>.Default checks whether type T implements the IComparable interface. If type T does not implement either interface, Comparer<T>.Default throws an InvalidOperationException.

List<Employee> employeeList = new List<Employee>();

employeeList.Add(new Employee("100", "Derrel Brill", 22, 53000));
employeeList.Add(new Employee("300", "Dave Gate", 30, 60000));
employeeList.Add(new Employee("200", "Jimmy Renaud", 55, 80000));
employeeList.Sort(new EmployeeNameComparer());

foreach (Employee e in employeeList)
{
    Console.WriteLine(e.ToString());

Comments

You must Sign In to comment on this topic.


© 2024 Digcode.com