When using the List<T>.Sort() method, there are cases where you want to sort based on a specific property or a special calculation formula, rather than the default order (e.g., ascending order).
Implementing IComparable<T> on the class itself changes the “default order” of that class. However, by creating a separate class that implements the IComparer<T> interface and passing it to the Sort method, you can apply a specific sort order only for that instance.
In this article, I will explain this using a scenario where schedule data with different dates is sorted “in order of earliest time (hour/minute), regardless of the date.”
Table of Contents
- Overview of the
IComparer<T>Interface - Practical Code Example: Sorting by Time Ignoring Date
- Execution Result
- Technical Points and Applications
- Reusability of Comparison Logic
- Simplification with
Comparison<T>Delegate - Difference from LINQ’s
OrderBy
- Summary
Overview of the IComparer<T> Interface
IComparer<T> provides a method named Compare for comparing two objects.
int Compare(T x, T y)
The rules for the return value are the same as IComparable<T>:
- Negative value: x is smaller than y (comes before).
- 0: x and y are equal.
- Positive value: x is larger than y (comes after).
Practical Code Example: Sorting by Time Ignoring Date
The following code is an example of creating and using a dedicated comparison class TimeOfDayComparer to sort a list of schedules spanning several days into “earliest time of day” order.
using System;
using System.Collections.Generic;
namespace ScheduleApp
{
class Program
{
static void Main()
{
// List of schedules with scattered dates
var schedules = new List<DateTime>
{
new DateTime(2025, 10, 1, 19, 30, 0), // 10/1 19:30
new DateTime(2025, 10, 2, 09, 00, 0), // 10/2 09:00
new DateTime(2025, 10, 1, 12, 15, 0), // 10/1 12:15
new DateTime(2025, 10, 3, 08, 45, 0), // 10/3 08:45
};
Console.WriteLine("--- Normal Sort (Date and Time) ---");
schedules.Sort(); // Default comparison includes the date
foreach (var dt in schedules)
{
Console.WriteLine(dt.ToString("MM/dd HH:mm"));
}
Console.WriteLine("\n--- Custom Sort (Time Only) ---");
// Sort by passing an instance of the custom Comparer
schedules.Sort(new TimeOfDayComparer());
foreach (var dt in schedules)
{
Console.WriteLine(dt.ToString("MM/dd HH:mm"));
}
}
}
/// <summary>
/// Class to compare only by time, ignoring the date part of DateTime.
/// </summary>
public class TimeOfDayComparer : IComparer<DateTime>
{
public int Compare(DateTime x, DateTime y)
{
// Get TimeOfDay property (TimeSpan type) and compare.
// This ensures size judgment is based only on time, even if year/month/day differ.
return x.TimeOfDay.CompareTo(y.TimeOfDay);
// Reference: If you want to compare only by Hour:
// return x.Hour.CompareTo(y.Hour);
}
}
}
Execution Result
--- Normal Sort (Date and Time) ---
10/01 12:15
10/01 19:30
10/02 09:00
10/03 08:45
--- Custom Sort (Time Only) ---
10/03 08:45
10/02 09:00
10/01 12:15
10/01 19:30
Technical Points and Applications
1. Reusability of Comparison Logic
The advantage of creating a class implementing IComparer<T> (like TimeOfDayComparer here) is that the comparison logic can be reused as a component. This is very convenient when the same sort order is needed in multiple places or when performing unit tests.
2. Simplification with Comparison<T> Delegate
If you do not plan to reuse the comparison logic and only need it once, you can write it concisely using a lambda expression (Comparison<T> delegate) without defining a separate class.
// Pass comparison logic directly via lambda expression
schedules.Sort((x, y) => x.TimeOfDay.CompareTo(y.TimeOfDay));
3. Difference from LINQ’s OrderBy
List<T>.Sort sorts the list itself (in-place processing). On the other hand, LINQ’s OrderBy method does not modify the original list but returns a new sequence that is sorted. You can also use IComparer<T> with OrderBy.
// Usage with LINQ
var sortedQuery = schedules.OrderBy(x => x, new TimeOfDayComparer());
Summary
By implementing IComparer<T>, you can enable sorting from a unique perspective that differs from the object’s default order. This is the standard technique for achieving flexible sorting according to requirements, such as “ignoring dates,” “case-insensitive sorting,” or “sorting by specific ID order.”
