Enumerable.Cast<T>() 將非泛型 IEnumerable 轉為泛型 IEnumerable<T>,解鎖 LINQ 查詢能力。許多開發者以為它只用於 ArrayList,實則廣泛適用於 WinForms/WPF 控制項集合、舊版 API 回傳值等場景。
// ListBox/ComboBox Items
var employees = listBox.Items.Cast<Employee>();
var count = employees.Count();
var hasBob = employees.Any(e => e.FirstName == "Bob");
// DataGridView 選取列/欄/儲存格
var selectedRows = dataGridView.SelectedRows.Cast<DataGridViewRow>();
var selectedCols = dataGridView.SelectedColumns.Cast<DataGridViewColumn>();
var selectedCells = dataGridView.SelectedCells.Cast<DataGridViewCell>();
// TreeNode 集合
var allNodes = treeView1.Nodes.Cast<TreeNode>();
var recursiveNodes = treeNode.Nodes.Cast<TreeNode>();
// 任意控制項的 Controls 集合
var allControls = this.Controls.Cast<Control>();
var textBoxes = this.Controls.Cast<Control>().OfType<TextBox>();
// 計算選取員工平均年齡
int avgAge = dataGridView.SelectedRows
.Cast<DataGridViewRow>()
.Select(r => (Employee)r.DataBoundItem)
.Average(e => e.Age);
// 統計選取員工所在城市 (去重)
var cities = dataGridView.SelectedRows
.Cast<DataGridViewRow>()
.Select(r => (Employee)r.DataBoundItem)
.Select(e => e.City)
.Distinct()
.ToArray();
public class A { }
public class B : A { }
// 子類陣列 -> 父類陣列 (安全)
B[] bArray = { new B(), new B() };
A[] aArray = bArray.Cast<A>().ToArray(); // OK
// 父類陣列 -> 子類陣列 (風險)
A[] mixed = { new A(), new A(), new B() };
// B[] bArray2 = mixed.Cast<B>().ToArray(); // 執行期拋出 InvalidCastException
// 解決方案:使用 OfType<T> 過濾兼容型別
B[] bArray2 = mixed.OfType<B>().ToArray(); // 僅保留 B 實例,安全無例外
| 操作 | 行為 | 適用場景 |
|---|---|---|
Cast<T>() | 強制轉型,失敗拋例外 | 確定所有元素皆為 T 或其衍生類 |
OfType<T>() | 過濾並轉型,忽略不匹配元素 | 混合型別集合,只需特定型別 |
(T)item | 編譯期/執行期強制轉型 | 單一元素確定型別 |
// 基本型別轉換
int i = (int)1.5; // OK: 隱含/明確轉換
double d = 10; // OK
// 但 Cast<T> 不支援值型別之間的數值轉換!
int[] ints = { 1, 2, 3 };
// double[] doubles = ints.Cast<double>().ToArray(); // 執行期失敗!
// 原因:Cast 內部執行 (T)enumerator.Current,值型別需拆箱為確切型別才能轉型
// 解決:使用 Select 轉換
double[] doubles = ints.Select(x => (double)x).ToArray(); // OK
Cast<T> 使用 CastIterator 實現,核心邏輯:
// 簡化版
public static IEnumerable<TResult> Cast<TResult>(this IEnumerable source)
{
foreach (object obj in source)
yield return (TResult)obj; // 直接強制轉型
}
這解釋了為何值型別數值轉換失敗:object 裝箱後為 int,無法直接拆箱為 double。
OfType<T>() 更安全。ints.Select(x => (double)x) 替代 Cast<double>()。Cast<T> 是連結舊式非泛型 API 與現代 LINQ 生態的關鍵橋樑。掌握其適用邊界、轉換規則與替代方案,能讓你在維護舊專案、操作 UI 控制項樹時游刃有餘。