LINQ 排序運算子提供強大的多層級排序能力,對應 SQL ORDER BY。本文系統整理 OrderBy/ThenBy、倒序版本、Reverse、自訂比較器與 IOrderedEnumerable 型別系統的實務陷阱。
| 方法 | SQL 對應 | 輸入型別 | 輸出型別 |
|---|---|---|---|
OrderBy | ORDER BY key ASC | IEnumerable<T> | IOrderedEnumerable<T> |
OrderByDescending | ORDER BY key DESC | IEnumerable<T> | IOrderedEnumerable<T> |
ThenBy | , key ASC | IOrderedEnumerable<T> | IOrderedEnumerable<T> |
ThenByDescending | , key DESC | IOrderedEnumerable<T> | IOrderedEnumerable<T> |
Reverse | 無對應 | IEnumerable<T> | IEnumerable<T> |
string[] names = { "James", "Jack", "Todd", "David", "Kobe" };
// 單鍵:按長度升冪
var q1 = names.OrderBy(n => n.Length);
// 結果: Jack(4), Todd(4), Kobe(4), James(5), David(5) - 同長度順序不確定
// 多鍵:長度升冪,同長度再按字母升冪
var q2 = names.OrderBy(n => n.Length).ThenBy(n => n);
// 結果: Jack, Kobe, Todd, David, James
// 串聯多個 ThenBy:長度 → 第2字元 → 第1字元
var q3 = names.OrderBy(n => n.Length)
.ThenBy(n => n[1])
.ThenBy(n => n[0]);
// 查詢運算式語法 (等效)
var q4 = from n in names
orderby n.Length, n[1], n[0]
select n;
// 價格降冊,同價格再按描述升冊
var q = ctx.Purchases
.OrderByDescending(p => p.Price)
.ThenBy(p => p.Description);
// 查詢運算式
var q2 = from p in ctx.Purchases
orderby p.Price descending, p.Description
select p;
僅限 **LINQ to Objects (本地查詢)**。LINQ to SQL 不支援,排序邏輯由資料庫 Collation 決定。
// 大小寫無關排序
var q = names.OrderBy(n => n, StringComparer.CurrentCultureIgnoreCase);
// 查詢運算式不支援傳入 comparer,LINQ to SQL 同理
// 解決方案:在 keySelector 內正規化
var qSql = from p in ctx.Purchases
orderby p.Description.ToUpper()
select p;
OrderBy 回傳 IOrderedEnumerable<T> (或 IOrderedQueryable<T>),此介面**專門允許後續串接 ThenBy** 編譯期檢查。
var 掩蓋型別錯誤
IOrderedEnumerable q1 = names.OrderBy(n => n.Length); // 明確型別
IOrderedEnumerable q2 = q1.ThenBy(n => n); // ✓ 編譯通過
// 若用 IEnumerable 接收 q1
IEnumerable q1bad = names.OrderBy(n => n.Length);
var q2bad = q1bad.ThenBy(n => n); // ✗ 編譯錯誤:IEnumerable 無 ThenBy
Where 破壞 IOrderedEnumerable 鏈
var q = names.OrderBy(n => n.Length); // q 是 IOrderedEnumerable
q = q.Where(n => n.Length > 3); // ✗ 編譯錯誤:Where 回傳 IEnumerable,無法指派回 var (IOrderedEnumerable)
// 解決:OrderBy 後立即呼叫 AsEnumerable() 降級
var qFixed = names.OrderBy(n => n.Length).AsEnumerable(); // 現在是 IEnumerable
qFixed = qFixed.Where(n => n.Length > 3); // ✓
// LINQ to SQL 同理:AsQueryable()
var qSql = ctx.Purchases.OrderBy(p => p.Price).AsQueryable();
qSql = qSql.Where(p => p.Price > 100);
var reversed = names.Reverse(); // 完全反轉順序,非排序
// 無 SQL 對應,僅本地集合適用
orderby a, b, c),可讀性最高、型別安全。IOrderedEnumerable 傳遞規則。Where/Select 等非排序運算子 → 緊接 AsEnumerable()/AsQueryable() 降級。Collation 決定,應用層勿嘗試覆蓋 (改用 ToUpper() 正規化鍵)。