C# List Sort: List.Sort vs LINQ OrderBy
c# list sort: Learn how to sort List<T> in C# using List.Sort and LINQ OrderBy, including custom comparers, multiple keys, and performance tradeoffs.
c# list sort requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.
When you need to order a List<T> in C#, the framework gives you two primary approaches: the instance method List<T>.Sort() and the LINQ extension methods OrderBy() and OrderByDescending(). They look similar but behave differently in ways that matter for memory, stability, and how you chain operations. This article walks through each approach, shows how to apply custom comparers, and explains the tradeoffs so you can pick the right tool for the job.
Using List.Sort() for In-Place Sorting
The List<T>.Sort() method sorts the elements of the list in place, meaning the original list is reordered and no new list is created. It uses the default comparer for the type T unless you supply one.
var numbers = new List<int> { 5, 2, 8, 1, 9 }; numbers.Sort(); // numbers is now { 1, 2, 5, 8, 9 }
For a list of strings, the default comparer performs a culture-sensitive comparison. That can be surprising when you expect ordinal ordering, especially with mixed-case or accented characters. If you need a predictable, ordinal sort, pass StringComparer.Ordinal explicitly:
var words = new List<string> { "apple", "Banana", "cherry" }; words.Sort(StringComparer.Ordinal);
List.Sort() is an unstable sort for most implementations. That means if two elements compare as equal, their original relative order is not guaranteed to be preserved. This is rarely a problem for simple value types, but it becomes important when sorting objects by a key that is not unique.
Sorting with LINQ OrderBy and OrderByDescending
LINQ's OrderBy and OrderByDescending return a new IEnumerable<T> that yields elements in sorted order. They do not modify the original list. This is a functional style: you project a sorted sequence without mutating the source.
var numbers = new List<int> { 5, 2, 8, 1, 9 }; var sorted = numbers.OrderBy(n => n).ToList(); // numbers is unchanged; sorted is { 1, 2, 5, 8, 9 }
Because OrderBy is deferred, the sort is not executed until you enumerate the result. Calling ToList() forces the enumeration and materializes a new list. If you only need to iterate once, you can skip ToList() and use the IEnumerable directly.
OrderBy performs a stable sort. Equal elements retain their original order. This is a significant difference from List.Sort(). When you sort by a non-unique key, stable sorting gives you predictable results without needing a secondary sort key.
Custom Sorting with IComparer and Comparison<T>
Both List.Sort() and OrderBy accept custom comparison logic. For List.Sort(), you can pass an IComparer<T> or a Comparison<T> delegate. For OrderBy, you provide a key selector and optionally an IComparer<T> for that key.
Consider a Person class with Name and Age properties. To sort by Age ascending using List.Sort() with a Comparison<T>:
people.Sort((a, b) => a.Age.CompareTo(b.Age));
If you need the same comparison in multiple places, define an IComparer<Person>:
public class AgeComparer : IComparer<Person> { public int Compare(Person x, Person y) { return x.Age.CompareTo(y.Age); } } people.Sort(new AgeComparer());
With LINQ, you can sort by a property directly:
var sortedPeople = people.OrderBy(p => p.Age).ToList();
If the default comparison for the key type is not what you need, supply a custom comparer:
var sortedByName = people.OrderBy(p => p.Name, StringComparer.OrdinalIgnoreCase).ToList();
Sorting by Multiple Keys
Real-world sorting often requires a primary and secondary key. With List.Sort(), you combine comparisons in a single delegate:
people.Sort((a, b) => { int result = a.Age.CompareTo(b.Age); return result != 0 ? result : a.Name.CompareTo(b.Name); });
With LINQ, you chain ThenBy or ThenByDescending after OrderBy:
var sorted = people .OrderBy(p => p.Age) .ThenBy(p => p.Name) .ToList();
ThenBy preserves the stable order of the primary key, so it is often cleaner than writing a composite comparer. For descending secondary keys, use ThenByDescending.
Stability and Performance Considerations
Stability matters when you sort by a key that is not unique. List.Sort() is unstable, so two people with the same age may appear in any order after sorting. OrderBy is stable, so the original order of equal elements is preserved. If your application relies on the original order for ties, use LINQ's OrderBy or add a secondary sort key to List.Sort().
Performance also differs. List.Sort() sorts in place and typically uses less memory because it does not allocate a new list. It is an introsort implementation, which is O(n log n) on average. OrderBy creates an iterator and, when materialized with ToList(), allocates a new list and uses a stable sort algorithm (often a merge sort). The extra allocation can be significant for large lists, but the difference is rarely noticeable for typical in-memory collections.
Another consideration is whether you need the original list later. If you do, OrderBy is safer because it leaves the source untouched. If you want to reduce memory pressure and you do not need the original order, List.Sort() is more efficient.
Choosing the Right Sorting Approach
The decision between List.Sort() and LINQ OrderBy depends on your specific requirements:
| Criterion | List.Sort() | LINQ OrderBy |
|---|---|---|
| Mutates original list | Yes | No |
| Stability | Unstable | Stable |
| Memory usage | In-place, minimal | Allocates new sequence and list |
| Syntax for multiple keys | Manual comparer | ThenBy / ThenByDescending |
| Deferred execution | No, immediate | Yes, until enumerated |
| Best for | Large lists, memory-sensitive code | Functional style, stable ordering |
Use List.Sort() when you own the list, want to avoid extra allocations, and do not need stable ordering. Use OrderBy when you need a stable sort, want to keep the original list unchanged, or prefer a declarative chain of sorting keys. Both are valid; the right choice depends on the context of your application.