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
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
// 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.95Sort by Multiple Properties
Use ThenBy() / ThenByDescending() to break ties with a secondary key.
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.
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.00Sort by String Property — Case-Insensitive
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.
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.
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.00OrderBy vs List.Sort — Quick Comparison
OrderBy() | List.Sort() | |
|---|---|---|
| Returns | New IOrderedEnumerable<T> | void (in-place) |
| Original list | Unchanged | Modified |
| Chaining | ThenBy(), ThenByDescending() | Pass Comparison<T> |
| LINQ pipeline | Yes — composes naturally | No |
| Allocation | New sequence object | None |
| Best for | LINQ queries, functional style | Performance-critical in-place sorts |