Implementing IComparable
Define a natural sort order with CompareTo.
Implementing IComparable is a free C# Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the C# Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Ordering With IComparable<T>
IComparable<T> defines a natural order for a type through a single method, CompareTo. Once implemented, your type can be sorted by Array.Sort, List.Sort, and ordered collections.
The CompareTo Contract
CompareTo returns a negative number if this instance is less than the other, zero if equal, and a positive number if greater. Comparing the underlying values often implements this directly.
using System;
public struct Weight : IComparable<Weight>
{
public int Grams;
public Weight(int g) { Grams = g; }
public int CompareTo(Weight other) => Grams.CompareTo(other.Grams);
public override string ToString() => Grams + "g";
}
public class Program
{
public static void Main()
{
Console.WriteLine(new Weight(100).CompareTo(new Weight(200)));
Console.WriteLine(new Weight(200).CompareTo(new Weight(200)));
Console.WriteLine(new Weight(300).CompareTo(new Weight(200)));
}
}Sorting a List
Once a type is comparable, List<T>.Sort() orders it using CompareTo with no extra arguments.
using System;
using System.Collections.Generic;
public struct Weight : IComparable<Weight>
{
public int Grams;
public Weight(int g) { Grams = g; }
public int CompareTo(Weight other) => Grams.CompareTo(other.Grams);
public override string ToString() => Grams + "g";
}
public class Program
{
public static void Main()
{
var list = new List<Weight> { new Weight(300), new Weight(100), new Weight(200) };
list.Sort();
Console.WriteLine(string.Join(", ", list));
}
}Comparing by a Primary Field
For a class, choose the field that defines its natural order. Here people are ordered by age, delegating to int.CompareTo.
using System;
using System.Collections.Generic;
public class Person : IComparable<Person>
{
public string Name;
public int Age;
public Person(string name, int age) { Name = name; Age = age; }
public int CompareTo(Person other) => Age.CompareTo(other.Age);
public override string ToString() => Name + "(" + Age + ")";
}
public class Program
{
public static void Main()
{
var people = new List<Person> { new Person("Ann", 30), new Person("Bo", 20) };
people.Sort();
Console.WriteLine(string.Join(", ", people));
}
}Tie-Breaking on Multiple Fields
When the primary key ties, compare a secondary field. Compute the first comparison and, only if it is zero, fall back to the next.
using System;
using System.Collections.Generic;
public class Person : IComparable<Person>
{
public string Name;
public int Age;
public Person(string name, int age) { Name = name; Age = age; }
public int CompareTo(Person other)
{
int byAge = Age.CompareTo(other.Age);
return byAge != 0 ? byAge : string.Compare(Name, other.Name, StringComparison.Ordinal);
}
public override string ToString() => Name + "(" + Age + ")";
}
public class Program
{
public static void Main()
{
var people = new List<Person> { new Person("Zoe", 30), new Person("Ann", 30) };
people.Sort();
Console.WriteLine(string.Join(", ", people));
}
}Sorting Arrays
Array.Sort also relies on IComparable<T>. The same comparison logic powers arrays, lists, and ordered structures alike.
using System;
public struct Score : IComparable<Score>
{
public int Points;
public Score(int p) { Points = p; }
public int CompareTo(Score other) => Points.CompareTo(other.Points);
public override string ToString() => Points.ToString();
}
public class Program
{
public static void Main()
{
var scores = new[] { new Score(50), new Score(10), new Score(30) };
Array.Sort(scores);
Console.WriteLine(string.Join(", ", (object[])scores));
}
}Descending Order
To reverse the order, invert the comparison by swapping the operands of CompareTo. This is a clean way to sort high-to-low without a separate comparer.
using System;
using System.Collections.Generic;
public struct Score : IComparable<Score>
{
public int Points;
public Score(int p) { Points = p; }
// Reversed: higher points come first
public int CompareTo(Score other) => other.Points.CompareTo(Points);
public override string ToString() => Points.ToString();
}
public class Program
{
public static void Main()
{
var list = new List<Score> { new Score(10), new Score(50), new Score(30) };
list.Sort();
Console.WriteLine(string.Join(", ", list));
}
}Comparable and Equatable Together
If CompareTo returns zero, the items are considered equal for ordering. Keep this consistent with Equals so sorting and equality agree.
using System;
public struct Version : IComparable<Version>, IEquatable<Version>
{
public int Major, Minor;
public Version(int major, int minor) { Major = major; Minor = minor; }
public int CompareTo(Version other)
{
int byMajor = Major.CompareTo(other.Major);
return byMajor != 0 ? byMajor : Minor.CompareTo(other.Minor);
}
public bool Equals(Version other) => CompareTo(other) == 0;
public override bool Equals(object obj) => obj is Version v && Equals(v);
public override int GetHashCode() => HashCode.Combine(Major, Minor);
}
public class Program
{
public static void Main()
{
Console.WriteLine(new Version(1, 2).CompareTo(new Version(1, 5)));
Console.WriteLine(new Version(2, 0).Equals(new Version(2, 0)));
}
}Using Comparison in LINQ OrderBy
Even without sorting in place, LINQ OrderBy can use your comparable type via a key selector, producing a sorted view lazily.
using System;
using System.Collections.Generic;
using System.Linq;
public struct Weight : IComparable<Weight>
{
public int Grams;
public Weight(int g) { Grams = g; }
public int CompareTo(Weight other) => Grams.CompareTo(other.Grams);
public override string ToString() => Grams + "g";
}
public class Program
{
public static void Main()
{
var items = new List<Weight> { new Weight(300), new Weight(100) };
foreach (var w in items.OrderBy(x => x))
Console.WriteLine(w);
}
}A Sortable Domain Type
Implementing IComparable<T> turns a domain concept into something the whole framework can order. Here tasks sort by priority then by name.
using System;
using System.Collections.Generic;
public class TaskItem : IComparable<TaskItem>
{
public string Name;
public int Priority;
public TaskItem(string name, int priority) { Name = name; Priority = priority; }
public int CompareTo(TaskItem other)
{
int byPriority = Priority.CompareTo(other.Priority);
return byPriority != 0 ? byPriority : string.Compare(Name, other.Name, StringComparison.Ordinal);
}
public override string ToString() => Priority + ":" + Name;
}
public class Program
{
public static void Main()
{
var tasks = new List<TaskItem>
{
new TaskItem("deploy", 2), new TaskItem("build", 1), new TaskItem("test", 1)
};
tasks.Sort();
Console.WriteLine(string.Join(", ", tasks));
}
}Try It Yourself
Make a multi-field comparable type and sort a list of it. The natural order falls out of CompareTo with no extra arguments.
using System;
using System.Collections.Generic;
public class Card : IComparable<Card>
{
public int Rank;
public string Suit;
public Card(int rank, string suit) { Rank = rank; Suit = suit; }
public int CompareTo(Card other)
{
int byRank = Rank.CompareTo(other.Rank);
return byRank != 0 ? byRank : string.Compare(Suit, other.Suit, StringComparison.Ordinal);
}
public override string ToString() => Rank + Suit;
}
public class Program
{
public static void Main()
{
var hand = new List<Card>
{
new Card(10, "H"), new Card(2, "S"), new Card(10, "C")
};
hand.Sort();
Console.WriteLine(string.Join(", ", hand));
}
}Quick Check
Recall the CompareTo contract.
Recap
IComparable<T> defines a natural order via CompareTo.
- Negative, zero, positive mean less-than, equal, greater-than.
- Enables
List.Sort,Array.Sort, and ordered collections. - Tie-break by comparing secondary fields.
- Keep
CompareTo == 0consistent withEquals.
Frequently asked questions
Is the “Implementing IComparable” lesson free?
Yes — the full text of “Implementing IComparable” is free to read here on the web, and the C# Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the C# Academy course, upgrade to CoddyKit PRO.
What will I learn in “Implementing IComparable”?
Define a natural sort order with CompareTo. You practise C# Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start C# Academy?
No prior experience is required. C# Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Implementing IComparable” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this C# Academy lesson?
Yes. Every C# Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Implementing IEquatable
- Overriding GetHashCode
- Implementing IComparable
- IComparer for Custom Sorting