as 運算子是 C# 進行安全參考轉型的核心工具,它在轉型失敗時回傳 null 而非拋出例外,極大簡化了防禦性程式碼。但其適用範圍有嚴格限制,誤用會導致編譯錯誤或邏輯缺陷。
運算式 as 目標類型 // 目標類型必須為:參考類型、介面、委派、可為 null 的值類型 (T?) // 結果類型永遠為目標類型
null → 回傳 nullnull (不拋出 InvalidCastException)| 場景 | 支援 | 說明 |
|---|---|---|
| 類別 → 基底類別/介面 | ✅ | 隱含參考轉型 |
| 基底類別 → 衍生類別 | ✅ | 顯式參考轉型 (可能回傳 null) |
| 實作介面 → 介面 | ✅ | 隱含參考轉型 |
| 介面 → 實作類別 | ✅ | 顯式參考轉型 |
| 陣列共變數 | ✅ | string[] as object[] |
| 值類型 → 介面 (裝箱) | ✅ | int as IComparable |
| 值類型 → 另一值類型 | ❌ | 編譯錯誤 (需用強制轉型) |
| 使用者定義轉型 | ❌ | 不支援自訂轉型運算子 |
| 非可為 null 的值類型 | ❌ | 目標類型不可為 null |
object obj = "Hello World"; // 安全轉型 string s = obj as string; // 成功:"Hello World" int? n = obj as int?; // 失敗:null (不拋出例外) // 錯誤用法:值類型目標 // int i = obj as int; // CS0413: 'int' 必須為可為 null 的型別 // 正確寫法 int? i = obj as int?; // null int j = (int)obj; // 拋出 InvalidCastException
interface ISerializable { void Serialize(); }
class Document : ISerializable { public void Serialize() => ... }
class Image { }
object[] items = { new Document(), new Image(), "text" };
foreach (var item in items)
{
// 安全介面轉型
var serializable = item as ISerializable;
if (serializable != null)
{
serializable.Serialize();
}
}
string[] strings = { "a", "b" };
object[] objects = strings as object[]; // 成功 (陣列共變數)
objects[0] = 123; // 執行時拋出 ArrayTypeMismatchException!
// as 只檢查參考相容性,不保證元素型別安全
| 維度 | as | (T)obj | is T (C# 7+) |
|---|---|---|---|
| 失敗行為 | 回傳 null | 拋出例外 | 回傳 false |
| 適用類型 | 參考/介面/可為 null 值類型 | 所有 | 所有 |
| 變數宣告 | 需額外行 | 需額外行 | 內建宣告 if (x is T t) |
| 效能 | 單次轉型 | 單次轉型 | 單次檢查 (最佳) |
| 可讀性 | 中等 | 低 (隱含風險) | 高 (現代語法) |
// 舊式 as 模式
var btn = sender as Button;
if (btn != null) { btn.Click += ...; }
// 現代 is 模式 (C# 7.0+) — 更安全、簡潔
if (sender is Button btn) { btn.Click += ...; }
// 進階:switch 運算式
var result = sender switch
{
Button b => HandleButton(b),
TextBox t => HandleTextBox(t),
_ => DefaultHandler()
};
as 編譯為 isinst 指令 — 單指令型別檢查 + 條件複製castclass — 檢查失敗拋出例外is 編譯為 isinst + brfalse — 無副作用is 模式避免雙重檢查 (as + null check)(obj as StringBuilder).Append("x") → 可能拋出 NullReferenceExceptiondouble d = 1.5; int i = d as int; → 編譯錯誤var dt = obj as DateTime; → 編譯錯誤 (DateTime 為值類型)object[] arr = stringArr as object[]; 後寫入非字串 → 執行時例外is 型別模式 — 宣告變數、範圍限定、零成本as 仍為可接受選項is + 強制轉型或 TryParse 等as 仍常見,配合 ?? 或 ?. 使用理解 as 的語意邊界與現代替代方案,能避免隱性 Bug 並編寫更符合當代 C# 風格的程式碼。