LINQ 查詢定義時不執行,只有在迭代結果 (foreach、ToList、Count 等) 時才真正計算。這帶來兩大優勢:
int[] numbers = { 10, 11, 12, 13, 14 };
Console.WriteLine($"查詢構建: {DateTime.Now}");
var query = from n in numbers select DoubleIt(n);
Console.WriteLine($"查詢構建完成: {DateTime.Now}"); // 幾乎同時
foreach (var result in query)
{
Console.WriteLine($"結果: {result}, 時間: {DateTime.Now}");
}
int DoubleIt(int x)
{
Console.WriteLine($" 計算: {x}");
Thread.Sleep(1000);
return x * 2;
}
/* 輸出:
查詢構建: 22:41:50
查詢構建完成: 22:41:50
計算: 10
結果: 20, 時間: 22:41:51
計算: 11
結果: 22, 時間: 22:41:52
...
*/
int[] data = { 10, 11, 12 };
var query = from n in data select n * 2;
foreach (var v in query) Console.WriteLine(v); // 20, 22, 24
// 修改源數據
for (int i = 0; i < data.Length; i++) data[i] += 10;
foreach (var v in query) Console.WriteLine(v); // 40, 42, 44 - 自動反映變更!
查詢實際在 GetEnumerator() 中執行,每次 MoveNext() 產生一個元素。
public static IEnumerable<int> MyQuery(int[] source)
{
foreach (var n in source)
{
Console.WriteLine($" Yield: {n}");
yield return n * 2; // 狀態機,逐個產生
}
}
// 使用
foreach (var v in MyQuery(new[] { 1, 2, 3 }))
Console.WriteLine(v);
/* 輸出交替:
Yield: 1
2
Yield: 2
4
...
*/
某些場景需要立即物化結果:
ToList()、ToArray():緩存到記憶體Count()、Sum()、First():聚合操作需遍歷全集合var query = from n in numbers select DoubleIt(n);
var list = query.ToList(); // 這裡一次性計算完所有
// 之後再用 list 不會重複計算
Console.WriteLine(list.Count); // 直接返回 5,不再調用 DoubleIt
.NET 1.x 時代的 ArrayList 等非泛型集合,元素類型為 object,無法直接 LINQ 查詢。
var list = new ArrayList { 1, 2, 3, 4, 5 };
var query = from int n in list select n; // 隱式 Cast<int>()
foreach (var n in query) Console.WriteLine(n); // 正常
var mixed = new ArrayList { 1, 2, "abc", 3, 4 };
var query = from int n in mixed.OfType<int>() select n;
foreach (var n in query) Console.WriteLine(n); // 1, 2, 3, 4 - "abc" 被靜默忽略
// 若用 Cast<int>() 會拋 InvalidCastException
| 方法 | 行為 | 適用場景 |
|---|---|---|
Cast<T>() | 全部轉型,失敗拋異常 | 確定集合內全是 T 類型 |
OfType<T>() | 只保留 T 類型,其他忽略 | 混合類型集合,安全篩選 |
from T x in list | 編譯器自動插入 Cast<T>() | 語法糖,等同 Cast |
ToList()掌握延遲執行與非泛型兼容,能讓 LINQ 在新舊代碼庫中游刃有餘。