How it works
IComparable<T> defines an ordering relationship for a type. CompareTo returns a negative value when this instance precedes the other, zero when they compare equal for ordering, and a positive value when it follows. Callers should use the sign rather than assume the result is exactly negative one or positive one.
An ordering rule often needs more than one field. A priority queue report might sort first by priority and then by a stable identifier when priorities match. An explicit tie-breaker makes output reproducible and helps sorted collections distinguish values that the domain considers distinct. Keep ordering and equality consistent with the intended collection contract, especially when a sorted set treats comparison zero as a duplicate.
Walk through the example. Ticket compares Priority first and then compares Code ordinally. The prepared list includes B and A at the same priority plus C at a lower priority. Sorting puts C first, then A, then B. The code comparison provides the tie-breaker regardless of the original insertion order. Null is defined as preceding an ordinary Ticket by returning a positive result when the other argument is null.
Watch the boundary. A comparison that returns zero for every equal-priority ticket can collapse distinct tickets in a SortedSet, even if their identifiers differ. That may be correct for one domain and wrong for another, so do not treat a display-only sort key as an identity contract accidentally. Also avoid subtracting arbitrary integer priorities as a comparison result, since subtraction can overflow; use CompareTo instead.
Practice independently. Add a second priority field or a creation sequence and define a three-part ordering rule. Test values differing at each level, plus identical values. Insert them in several orders and verify the same sorted report. Then compare using the rule for a sorted list and for a sorted set, and explain why the meaning of comparison zero becomes especially important in the latter.
A working example
using System;
using System.Collections.Generic;
using System.Linq;
using System.Globalization;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
var tickets = new List<Ticket> { new("B", 2), new("C", 1), new("A", 2) };
tickets.Sort();
foreach (var item in tickets) Console.WriteLine($"{item.Code}:{item.Priority}");
sealed class Ticket : IComparable<Ticket>
{
public string Code { get; }
public int Priority { get; }
public Ticket(string code, int priority) { Code = code; Priority = priority; }
public int CompareTo(Ticket? other)
{
if (other is null) return 1;
int priority = Priority.CompareTo(other.Priority);
return priority != 0 ? priority : StringComparer.Ordinal.Compare(Code, other.Code);
}
}
Expected output
C:1 A:2 B:2
Compare by sign, add deliberate tie-breakers, and understand how sorted sets interpret zero.