Join/GroupJoin 在 LINQ to Objects 內部採用**兩階段機制**:建立 Lookup → 合併外部序列。理解 ILookup 可手工優化重複連接、實現自訂連接邏輯。
public interface ILookup : IEnumerable>, IEnumerable {
int Count { get; }
bool Contains(TKey key);
IEnumerable this[TKey key] { get; } // 索引器:鍵 → 元素序列
}
IDictionary 單一值)。
// 將所有購買單依 CustomerID 建立 Lookup
ILookup purchLookup = purchases
.ToLookup(p => p.CustomerID, p => p);
// 參數 1: keySelector | 參數 2: elementSelector (可省略,預設為元素本身)
// 索引器回傳匹配元素序列 (非單一元素)
foreach (var p in purchLookup[1]) { // 客戶 ID = 1 的所有訂單
Console.WriteLine(p.Description);
}
// 不存在鍵回傳空序列 (不拋出例外)
var empty = purchLookup[999]; // Empty IEnumerable
var q = from c in customers
from p in purchLookup[c.ID] // 直接索引查找
select new { c.Name, p.Description, p.Price };
// 效能與 Join 相同,適用於**多次重用同一 Lookup** 的場景
var q = from c in customers
from p in purchLookup[c.ID].DefaultIfEmpty()
select new {
c.Name,
Descript = p == null ? null : p.Description,
Price = p == null ? (decimal?)null : p.Price
};
var q = from c in customers
select new {
CustName = c.Name,
CustPurchases = purchLookup[c.ID] // 直接取得群組序列
};
// 場景:多個查詢都需連接 purchases
var purchLookup = purchases.ToLookup(p => p.CustomerID);
// 查詢 1:高價值訂單客戶
var q1 = from c in customers
from p in purchLookup[c.ID]
where p.Price > 1000
select c.Name;
// 查詢 2:最近訂單客戶
var q2 = from c in customers
from p in purchLookup[c.ID]
where p.Date > DateTime.Now.AddDays(-30)
select c.Name;
// 查詢 3:GroupJoin 風格報表
var q3 = from c in customers
select new { c.Name, Orders = purchLookup[c.ID] };
手工 ToLookup 將「建立雜湊表」與「連接邏輯」分離,**避免重複建立查找表**,適合同一內部集合被多次連接的批次報表/分析查詢。
join/GroupJoin,翻譯為 SQL JOIN,資料庫優化器處理,**無需手工 Lookup**。join (自動優化)。ToLookup 快取,後續用 SelectMany/Select 索引。SelectMany + where。