select 子句是 LINQ 查詢的「輸出塑形器」,決定查詢結果的結構與內容。本文透過四大實戰範例,系統化展示 select 的核心用法與進階技巧。
對查詢結果逐元素套用轉換邏輯:
string[] names = { "Jack", "Bob", "Bill", "Catty", "Willam" };
var rs = from n in names
where n.StartsWith("B")
select n.ToLower(); // 投影:轉小寫
foreach (var r in rs) Console.WriteLine(r);
// 輸出: bob, bill
💡 select 後的運算式可為任意 C# 運算式:方法呼叫、運算子、三元運算子、物件初始化器等。
LINQ to SQL 查詢資料庫時,僅選取必要欄位可大幅降低網路傳輸與記憶體佔用:
var ctx = new NorthwindDataContext();
var cs = from c in ctx.Customers
where c.ContactName.StartsWith("B")
select new
{
CustomerID = c.CustomerID,
CustomerName = c.ContactTitle + " " + c.ContactName
};
foreach (var c in cs) Console.WriteLine(c);
// 輸出: { CustomerID = BERGS, CustomerName = Order Administrator Beate Vileid } ...
c.CustomerID → CustomerID)。record 最適合)。陣列/List<T> 等索引集合,可在 Select 取得元素索引:
string[] names = { "Jack", "Bob", "Bill", "Catty", "Willam" };
var rs = names.Select((name, index) => new { Name = name, Index = index });
foreach (var r in rs) Console.WriteLine($"{r.Index}: {r.Name}");
// 輸出:
// 0: Jack
// 1: Bob
// 2: Bill
// 3: Catty
// 4: Willam
⚠️ 查詢語法from ... select**不支援** 索引參數,必須使用方法語法Select((x,i) => ...)。
相當於雙重迴圈或 SQL CROSS JOIN / INNER JOIN:
int[] numbersA = { 0, 2, 4, 5, 6, 8, 9 };
int[] numbersB = { 1, 3, 5, 7, 8 };
var pairs = from a in numbersA
from b in numbersB
where a < b
select new { a, b };
foreach (var p in pairs) Console.WriteLine($"{p.a} < {p.b}");
// 等同 numbersA.SelectMany(a => numbersB.Where(b => a < b).Select(b => new {a,b}))
var ctx = new NorthwindDataContext();
var rs = from c in ctx.Customers
from o in c.Orders // 導航屬性 Orders 對應 FK 關聯
where o.ShipCity.StartsWith("B")
select new { CustomerName = c.ContactName, OrderID = o.OrderID };
foreach (var r in rs) Console.WriteLine(r);
// 產生 SQL 含 INNER JOIN,非記憶體端 Cross Join
| 模式 | 語法範例 | 適用場景 |
|---|---|---|
| 單值投影 | select x.Name | 只需單一欄位/屬性 |
| 匿名型別 | select new { x.Id, x.Name } | DTO 形狀、減少載入欄位 |
| 具名型別 | select new CustomerDto { Id=x.Id, Name=x.Name } | 跨層傳遞、序列化 |
| 索引投影 | .Select((x,i) => new {x, i}) | 需要知道元素位置 |
| 扁平化 | from a in A from b in a.Bs select b | 一對多展開 (SelectMany) |
| 條件投影 | select x.Active ? x.Name : "N/A" | 依條件輸出不同值 |
| 巢狀投影 | select new { x.Name, Children = x.Children.Select(c => c.Name) } | 階層式 DTO |
AsEnumerable() 後) 執行,避免轉譯為 SQL 失敗或效能差。select new { FullName = c.FirstName + " " + c.LastName } 可轉譯;select new { FullName = FormatName(c.FirstName, c.LastName) } (自訂方法) 會拋出例外。select new { ... } 回傳匿名型別/DTO,**非實體型別**,DataContext 不會追蹤變更,SubmitChanges() 不會持久化。select 是 LINQ 查詢的「出口」,掌握匿名型別投影、索引感知、多來源扁平化三大核心模式,即可靈活應對從簡單格式化到複雜關聯查詢的各種輸出需求。記住:**查詢語法適合單來源投影,方法語法解鎖索引與 SelectMany 進階能力**。