Select 與 SelectMany 是 LINQ 核心投影運算子,對應 SQL SELECT 與 CROSS/OUTER APPLY (或扁平化連接)。本文系統整理語法、索引投影、子查詢建構物件層級、雙重延遲執行特性。
// 來源:IEnumerable → 目標:IEnumerable
// 元素數量不變,每個元素經選擇器函數轉換
// 1. 基本投影:列出所有字型名稱
var fonts = from f in FontFamily.Families
select f.Name;
// 2. 匿名型建構多欄位
var q = from f in FontFamily.Families
select new {
f.Name,
LineSpacing = f.GetLineSpacing(FontStyle.Bold)
};
// 3. 無轉換投影 (僅篩選)
var q = from f in FontFamily.Families
where f.IsStyleAvailable(FontStyle.Strikeout)
select f; // 回傳 FontFamily 物件本身
選擇器接受第二個參數 int index (元素在序列中位置,**僅限 LINQ to Objects**):
string[] names = { "James", "Derek", "Harry", "Mary", "Jack" };
var q = names.Select((s, i) => i + "=" + s);
// 結果: "0=James", "1=Derek", "2=Harry", "3=Mary", "4=Jack"
IEnumerable (子序列)。CROSS APPLY / OUTER APPLY / 非等值連接。
// 範例:建立目錄-檔案兩層物件結構
DirectoryInfo[] dirs = new DirectoryInfo(@"C:\LINQ").GetDirectories();
var q = from d in dirs
where (d.Attributes & FileAttributes.System) == 0
select new {
DirectoryName = d.FullName,
Created = d.CreationTime,
Files = from f in d.GetFiles()
where (f.Attributes & FileAttributes.Hidden) == 0
select new { FileName = f.Name, f.Length }
};
// q 型別:IEnumerable<匿型別{ DirectoryName, Created, Files }>
// Files 型別:IEnumerable<匿型別{ FileName, Length }>
d) → **相關子查詢**。Files 序列**直到被枚舉時才執行** d.GetFiles()。| 場景 | 運算子 | 結果結構 |
|---|---|---|
| 單一元素轉換/篩選/建構匿名型別 | Select | 同基數扁平序列 |
| 一對多關聯、子集合展開、扁平化連接 | SelectMany | 合併後扁平序列 |
| 保留階層結構 (父含子集合屬性) | Select + 子查詢 | 巢狀物件圖 |
| LINQ to SQL 連接 | 優先 Join/GroupJoin | SQL JOIN 效能最佳 |
| 本地集合連接 | 優先 Join/GroupJoin | 雜湊連接 O(N+M) |
(s, i) => ... (無法翻譯為 SQL)。Files = ...) 可能導致 **N+1 查詢問題** 或客戶端評估,建議改用 Join 或預先載入 (DataLoadOptions.LoadWith)。