WinForms/WebForms 開發常需遍歷控制項集合並針對特定型別執行邏輯。本文系統說明 Control 基類轉換為衍生型別的三種主流方式:is 型別模式、as 運算子、switch 型別模式,並對比傳統 GetType() 字串比較的缺陷。
// 遍歷面板內所有控制項
foreach (Control ctrl in this.panel1.Controls)
{
// 需針對 TextBox、Button 執行不同邏輯
}
foreach (Control ctrl in this.panel1.Controls)
{
// 型別檢查 + 宣告變數,一步完成
if (ctrl is TextBox txt)
{
txt.Text = "處理 TextBox";
txt.BackColor = Color.Yellow;
}
else if (ctrl is Button btn)
{
btn.Text = "已處理";
btn.Enabled = false;
}
else if (ctrl is Panel pnl && pnl.Name == "PanelX")
{
pnl.Visible = false;
pnl.Enabled = false;
}
}
優點:語法簡潔、型別安全、自動轉換、支援模式比對條件 (如 pnl.Name == "PanelX")
foreach (Control ctrl in this.panel1.Controls)
{
TextBox txt = ctrl as TextBox;
if (txt != null)
{
txt.Text = "處理 TextBox";
continue;
}
Button btn = ctrl as Button;
if (btn != null)
{
btn.Text = "";
continue;
}
Panel pnl = ctrl as Panel;
if (pnl != null && pnl.Name == "PanelX")
{
pnl.Visible = false;
}
}
優點:C# 所有版本支援、邏輯明確
缺點:較冗長、需手動 null 檢查
foreach (Control ctrl in this.panel1.Controls)
{
switch (ctrl)
{
case TextBox txt:
txt.Text = "處理 TextBox";
break;
case Button btn:
btn.Text = "";
break;
case Panel pnl when pnl.Name == "PanelX":
pnl.Visible = false;
pnl.Enabled = false;
break;
case Panel pnl:
pnl.Visible = true;
pnl.Enabled = true;
break;
}
}
優點:多型別分派清晰、支援 when 守衛條件、結構化
// 反模式:脆弱、效能差、不支援繼承
if (ctrl.GetType().ToString() == "System.Windows.Forms.TextBox")
{
// 錯誤:繼承自 TextBox 的自訂類別會失敗
}
// 稍好但仍不支援繼承
if (ctrl.GetType() == typeof(TextBox))
{
// 自訂類別 MyTextBox : TextBox 會回傳 false
}
| 方法 | 支援繼承 | 語法 | 推薦度 |
|---|---|---|---|
is Type var | ✅ 是 | 簡潔 | ⭐⭐⭐⭐⭐ |
as Type | ✅ 是 | 中等 | ⭐⭐⭐⭐ |
switch (var) { case Type v: } | ✅ 是 | 結構化 | ⭐⭐⭐⭐⭐ |
GetType() == typeof(Type) | ❌ 否 (精確匹配) | 冗長 | ⭐⭐ |
GetType().Name == "TypeName" | ❌ 否 | 脆弱 | ⭐ |
public IEnumerable GetAllControls(Control root)
{
foreach (Control ctrl in root.Controls)
{
yield return ctrl;
foreach (var child in GetAllControls(ctrl))
yield return child;
}
}
// 使用
foreach (var ctrl in GetAllControls(this))
{
if (ctrl is TextBox txt) txt.Clear();
}
public void ValidateForm(Control container)
{
foreach (Control ctrl in container.Controls)
{
switch (ctrl)
{
case TextBox txt when string.IsNullOrWhiteSpace(txt.Text):
errorProvider.SetError(txt, "必填欄位");
break;
case ComboBox cmb when cmb.SelectedIndex < 0:
errorProvider.SetError(cmb, "請選擇項目");
break;
case CheckBox chk when !chk.Checked && chk.Tag?.ToString() == "Required":
errorProvider.SetError(chk, "必須勾選同意條款");
break;
}
}
}
public void ResetForm(Control container)
{
foreach (Control ctrl in container.Controls)
{
switch (ctrl)
{
case TextBox txt: txt.Clear(); break;
case ComboBox cmb: cmb.SelectedIndex = -1; break;
case CheckBox chk: chk.Checked = false; break;
case RadioButton rb: rb.Checked = false; break;
case DateTimePicker dtp: dtp.Value = DateTime.Today; break;
}
}
}
現代 C# (is 型別模式、switch 型別模式) 提供優雅、型別安全、支援繼承的控制項轉換方式。棄用 GetType() 字串比較與 typeof 精確匹配,拥抱模式比對語法,能寫出更健壯、可維護的 UI 邏輯代碼。