How to sort a list by property in C#

To sort a list of objects by a property in C#, use LINQ OrderBy() / OrderByDescending() or List.Sort() with a lambda. LINQ returns a new sorted sequence while List.Sort() sorts in place.

OrderBy() is the idiomatic choice when using LINQ elsewhere in your code. List.Sort() is marginally faster for large in-place sorts because it avoids allocating a new sequence.

Sort by a Single Property with LINQ

C# Example Code
var products = new List<Product>
{
    new Product("Keyboard", 49.99m),
    new Product("Monitor",  299.00m),
    new Product("Mouse",    24.95m),
    new Product("Webcam",   89.00m)
};

// Ascending — cheapest first
var byPriceAsc = products.OrderBy(p => p.Price).ToList();
foreach (var p in byPriceAsc)
    Console.WriteLine($"{p.Name}: {p.Price:C}");

// Output:
// Mouse:    $24.95
// Keyboard: $49.99
// Webcam:   $89.00
// Monitor:  $299.00

record Product(string Name, decimal Price);

Sort Descending

C# Example Code
// Descending — most expensive first
var byPriceDesc = products.OrderByDescending(p => p.Price).ToList();
foreach (var p in byPriceDesc)
    Console.WriteLine($"{p.Name}: {p.Price:C}");

// Output:
// Monitor:  $299.00
// Webcam:   $89.00
// Keyboard: $49.99
// Mouse:    $24.95

Sort by Multiple Properties

Use ThenBy() / ThenByDescending() to break ties with a secondary key.

C# Example Code
var employees = new List<Employee>
{
    new Employee("Alice",   "Engineering", 90_000m),
    new Employee("Bob",     "Marketing",   75_000m),
    new Employee("Charlie", "Engineering", 85_000m),
    new Employee("Diana",   "Marketing",   80_000m),
    new Employee("Eve",     "Engineering", 90_000m)
};

// Primary: department (A→Z), secondary: salary descending, tertiary: name (A→Z)
var sorted = employees
    .OrderBy(e => e.Department)
    .ThenByDescending(e => e.Salary)
    .ThenBy(e => e.Name)
    .ToList();

foreach (var e in sorted)
    Console.WriteLine($"{e.Department,-15} {e.Name,-10} {e.Salary:C}");

// Output:
// Engineering     Alice      $90,000.00
// Engineering     Eve        $90,000.00
// Engineering     Charlie    $85,000.00
// Marketing       Diana      $80,000.00
// Marketing       Bob        $75,000.00

record Employee(string Name, string Department, decimal Salary);

Sort in Place with List.Sort()

List.Sort() mutates the original list and avoids allocating a new collection — useful when memory pressure matters.

C# Example Code
var products = new List<Product>
{
    new Product("Keyboard", 49.99m),
    new Product("Monitor",  299.00m),
    new Product("Mouse",    24.95m)
};

// Sort in place by Price ascending
products.Sort((a, b) => a.Price.CompareTo(b.Price));

foreach (var p in products)
    Console.WriteLine($"{p.Name}: {p.Price:C}");

// Output:
// Mouse:    $24.95
// Keyboard: $49.99
// Monitor:  $299.00

Sort by String Property — Case-Insensitive

C# Example Code
var cities = new List<City>
{
    new City("berlin"),
    new City("Amsterdam"),
    new City("copenhagen"),
    new City("Brussels")
};

// Case-insensitive alphabetical sort
var sorted = cities
    .OrderBy(c => c.Name, StringComparer.OrdinalIgnoreCase)
    .ToList();

foreach (var c in sorted)
    Console.WriteLine(c.Name);

// Output:
// Amsterdam
// berlin
// Brussels
// copenhagen

record City(string Name);

Sort by Nullable Property

Handle null values explicitly so they sort to the beginning or end as needed.

C# Example Code
var tasks = new List<WorkTask>
{
    new WorkTask("Deploy",    DateTime.Parse("2025-06-01")),
    new WorkTask("Review",    null),                          // no due date
    new WorkTask("Test",      DateTime.Parse("2025-05-15")),
    new WorkTask("Plan",      null)
};

// Nulls last — push tasks without a due date to the end
var sorted = tasks
    .OrderBy(t => t.DueDate.HasValue ? 0 : 1)
    .ThenBy(t => t.DueDate)
    .ToList();

foreach (var t in sorted)
    Console.WriteLine($"{t.Name,-10} {t.DueDate?.ToString("yyyy-MM-dd") ?? "(no due date)"}");

// Output:
// Test       2025-05-15
// Deploy     2025-06-01
// Review     (no due date)
// Plan       (no due date)

record WorkTask(string Name, DateTime? DueDate);

Implement IComparable for Default Sort Order

If the same type is sorted repeatedly, implement IComparable<T> to define a natural ordering that List.Sort() uses automatically.

C# Example Code
public class Product : IComparable<Product>
{
    public string Name  { get; init; }
    public decimal Price { get; init; }

    public Product(string name, decimal price) => (Name, Price) = (name, price);

    // Natural order: cheapest first
    public int CompareTo(Product? other)
    {
        if (other is null) return 1;
        return Price.CompareTo(other.Price);
    }
}

var products = new List<Product>
{
    new("Keyboard", 49.99m),
    new("Monitor",  299.00m),
    new("Mouse",    24.95m)
};

products.Sort(); // uses CompareTo
foreach (var p in products)
    Console.WriteLine($"{p.Name}: {p.Price:C}");

// Output:
// Mouse:    $24.95
// Keyboard: $49.99
// Monitor:  $299.00

OrderBy vs List.Sort — Quick Comparison

OrderBy()List.Sort()
ReturnsNew IOrderedEnumerable<T>void (in-place)
Original listUnchangedModified
ChainingThenBy(), ThenByDescending()Pass Comparison<T>
LINQ pipelineYes — composes naturallyNo
AllocationNew sequence objectNone
Best forLINQ queries, functional stylePerformance-critical in-place sorts