當我們有一塊連續記憶體的資料結構(大小固定),就會有 Lengthf、values[i] 直接索引(O(1)),這是真正「已經全部在手上」的一批資料,適合需要高速索引、固定長度、確定要把資料一次放好的場景

2

它不是資料結構,它是「你可以被 foreach 一個個拿出來」的能力(介面),可能背後是 array / list / db query / 檔案讀取 / generator / yield return…,重點在於,它允許 deferred execution(延遲執行),也就是寫了 Select/Where 不代表立刻跑,是 foreach / ToList() / Count() 的那刻才真正開始取資料

這適合想要串接處理流程、資料可能很大、資料來源不是一次就全部拿到的場景(像 DB、串流、yield)

2

Enum.GetValues(Type) 是比較早期的 API,為了能支援「任何 enum 型別」,它回傳 Array(非泛型)。所以才需要

1
values.Cast<PromotionEngineTypeEnum>()

Cast<T>() 的作用是:把「非泛型可枚舉(IEnumerable)」裡面的每個元素,逐一轉成 T,變成 IEnumerable<T>,LINQ 才能舒服地用

3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[Flags]
public enum PromotionEngineTypeEnum
{
/// <summary>
/// 第N件打折
/// </summary>
DiscountNthPieceWithRate = 1,

/// <summary>
/// 第N件固定價
/// </summary>
DiscountNthPieceWithPrice = 2,

/// <summary>
/// 第N件折現
/// </summary>
DiscountNthPieceWithAmount = 4
}
1
2
3
var values = Enum.GetValues(typeof(PromotionEngineTypeEnum)); //// Array 非泛型集合
values.Cast<PromotionEngineTypeEnum>().Select(x => x.ToString()).Dump(); //// Cast 明確轉化為 IEnumeable<Enum>
//// 當然這裡也可以直接操作 foreach

用途例如

3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//// 檢查 request
var allowed = new HashSet<string>(
Enum.GetValues(typeof(PromotionEngineTypeEnum))
.Cast<PromotionEngineTypeEnum>()
.Select(e => e.ToString()),
StringComparer.OrdinalIgnoreCase);

string input = "DiscountNthPieceWithRate"; // 來自設定檔或 request

if (!allowed.Contains(input))
{
throw new ArgumentException($"Unknown PromotionEngineTypeEnum: {input}");
}


//// 把 enum 值做排序、過濾,再輸出到 log / Dump
var result = Enum.GetValues(typeof(PromotionEngineTypeEnum))
.Cast<PromotionEngineTypeEnum>()
.OrderBy(e => (int)e)
.Where(e => (int)e >= 2)
.Select(e => $"{(int)e} - {e}")
.ToList();

//// 後台 UI 下拉選單(value + text)
var items = Enum.GetValues(typeof(PromotionEngineTypeEnum))
.Cast<PromotionEngineTypeEnum>()
.Select(e => new
{
Value = (int)e,
Text = e.ToString()
})
.ToList();

4

其實 T[] 本身就能 LINQ

1
2
int[] a = { 1, 2, 3 };
a.Select(x => x * 2)

Enum.GetValues(typeof(PromotionEngineTypeEnum))不能直接 Select 的原因是 values 的型別是 Array(非泛型),我們需要先變成 IEnumerable<PromotionEngineTypeEnum> 才行

「第 N 個元素」→ 用 Array/List(索引能力強)

已經把資料撈完(或快取好)放在記憶體,現在要直接拿第 0、1、2…筆做事

1
2
3
4
5
6
7
8
9
var topUsers = new List<string> { "Allen", "Ben", "Cindy", "Dora" };

// 你要「第 3 名」(index=2)
var third = topUsers[2];

// 你要「最後一名」
var last = topUsers[^1];

// 你要「第 10 筆」:如果用 IEnumerable,你得走訪 10 次;List/Array 直接 O(1)

3

「串接一堆處理邏輯」→ 用 IEnumerable(管線思維)

只要近 7 天、付款成功、金額 > 1000 的訂單,最後只輸出你要的欄位
IEnumerable orders = GetOrdersFromSomewhere(); // 可能是記憶體、可能是 DB 查詢結果

1
2
3
4
5
6
var reportRows = orders
.Where(o => o.CreatedAt >= DateTime.Today.AddDays(-7))
.Where(o => o.PayStatus == PayStatus.Paid)
.Where(o => o.TotalAmount > 1000)
.OrderByDescending(o => o.TotalAmount)
.Select(o => new ReportRow(o.Id, o.MemberId, o.TotalAmount));

4

「馬上 materialize(實體化)成集合」→ 最後 ToArray() / ToList()

避免「資料來源很貴」被重複讀(DB / 檔案 / 網路)

1
2
3
4
5
6
7
8
var expensiveQuery = GetOrdersFromDb()      // 假設這裡會真的打 DB
.Where(o => o.PayStatus == PayStatus.Paid)
.ToList(); // 只查一次,結果固定

// 後面可以重複用,不會每次都再打 DB/再枚舉一次
var count = expensiveQuery.Count;
var max = expensiveQuery.Max(o => o.TotalAmount);

3

「避免一次載入大量資料」→ IEnumerable + 延遲執行(例如讀檔、DB query)

DB(EF Core)延遲執行:先組查詢,真的需要時才

1
2
3
4
5
6
7
8
9
10
// IQueryable 也是 IEnumerable 的一種「更強版本」(可以翻譯成 SQL)
var query = db.Orders
.Where(o => o.CreatedAt >= DateTime.Today.AddDays(-7))
.Where(o => o.PayStatus == PayStatus.Paid);

// 這裡才真的送到 DB 執行
var page = query
.OrderByDescending(o => o.CreatedAt)
.Take(50)
.ToList();

3

g

b