顯式參考轉換 ((T)expr) 是在參考類型層級結構中進行向下/橫向轉換的機制,編譯器允許但執行時期會驗證物件實際類型相容性。理解其適用場景、陣列/委派變異規則與失敗模式,對建立型別安全的多型系統至關重要。
(目標類型)運算式 // 編譯時期:允許潛在相容的參考類型轉換 // 執行時期:檢查實際物件是否為目標類型或其衍生類別 // 成功:回傳同一物件參考 (型別視角改變) // 失敗:拋出 InvalidCastException // null:永遠成功,回傳 null
| 類別 | 來源 → 目標 | 條件 | 執行檢查 | 範例 |
|---|---|---|---|---|
| 1. 物件 → 任意參考類型 | object → T | T 為參考類型 | 是 | object o = "hi"; (string)o |
| 2. 基底類別 → 衍生類別 | Base → Derived | 衍生自 Base | 是 | Animal a = new Dog(); (Dog)a |
| 3. 非密封基底類別 → 介面 | Base → IInterface | Base 未實作 IInterface,且非 sealed | 是 | class Base {} (ICloneable)new Base() |
| 4. 介面 → 非密封類別 | IInterface → Class | Class 未實作 IInterface,且非 sealed | 是 | ICloneable c = ...; (MyClass)c |
| 5. 介面 → 介面 (非繼承) | I1 → I2 | I1 非 I2 子介面 | 是 | IEnumerable e = ...; (IList)e |
| 6. 陣列轉換 | S[] → T[] | 同維度、元素皆參考類型、元素間有顯式參考轉換 | 是 (含元素檢查) | object[] → string[] |
| 7. 系統類別轉換 | Array → T[], Delegate → TDelegate, ICloneable → T[]/TDelegate | 特定系統類別 | 是 | Array arr = ...; (int[])arr |
class Animal { }
class Dog : Animal { }
class Cat : Animal { }
Animal a = new Dog(); // 隱含
Animal a2 = (Animal)new Dog(); // 顯式 (多餘但合法)
Animal a = new Dog();
Dog d = (Dog)a; // 成功:a 實際為 Dog
Animal a2 = new Cat();
Dog d2 = (Dog)a2; // InvalidCastException:a2 實際為 Cat
// 防禦性模式
if (a is Dog dog) { /* 使用 dog */ }
var dog2 = a as Dog; // null 安全
Dog d = new Dog(); Cat c = (Cat)(Animal)d; // InvalidCastException // 必須先向上到共同基底,再向下
interface ISerializable { }
interface ICloneable { }
class Document : ISerializable { }
class SerializableDoc : Document, ICloneable { }
ISerializable ser = new Document();
ICloneable clone = (ICloneable)ser; // InvalidCastException (Document 未實作 ICloneable)
ISerializable ser2 = new SerializableDoc();
ICloneable clone2 = (ICloneable)ser2; // 成功 (實際物件實作 ICloneable)
// 介面 → 類別 (類別未實作介面但非 sealed)
ICloneable c = new SerializableDoc();
Document doc = (Document)c; // 成功 (SerializableDoc 繼承 Document)
// 介面 → 介面 (非繼承關係)
IEnumerable enumObj = new List();
IList list = (IList)enumObj; // 成功 (List 實作 IList)
string[] strings = { "a", "b" };
object[] objects = strings; // 隱含轉換 (編譯器允許)
// 風險:寫入非預期類型
objects[0] = 123; // ArrayTypeMismatchException (執行時期檢查元素型別)
// 共變數安全前提:僅讀取元素
object[] objects = { "a", "b" }; // 實際元素為 string
string[] strings = (string[])objects; // 顯式轉換成功 (元素確實相容)
// 但若元素不相容
object[] objects2 = { "a", 123 };
string[] strings2 = (string[])objects2; // InvalidCastException (執行時期檢查)
// 規則總結
// 1. 維度相同
// 2. 元素類型皆為參考類型 (值類型陣列無共變數/逆變數)
// 3. 元素類型間有參考轉換關係 (隱含/顯式)
// 4. 執行時期檢查實際元素型別
// 多維陣列:同規則,維度數必須完全相同 string[,] matrix2 = new string[2,2]; object[,] objMatrix = matrix2; // 共變數 // 交錯陣列:外層陣列元素為陣列參考 string[][] jagged = new string[2][]; object[][] objJagged = jagged; // 共變數 (外層元素 string[] → object[])
// 委派參數逆變數:輸入類型可接受基底類別 Action
// Array → 具體陣列
Array arr = new int[10];
int[] intArr = (int[])arr; // 成功
// Delegate → 具體委派
Delegate del = new Action(() => { });
Action act = (Action)del; // 成功
// ICloneable → 陣列/委派 (歷史遺留)
ICloneable clone = new int[5];
int[] arr2 = (int[])clone; // 成功 (陣列實作 ICloneable)
castclass IL 指令TypeHandle 是否為目標類型或其子類型is 模式比對 — if (obj is Dog dog) 單次檢查、零例外、範圍安全as + null 檢查 — var dog = obj as Dog; if (dog != null) 避免例外開銷IReadOnlyList<T> 或不可變集合in/out 標記 — 編譯器靜態驗證,無執行開銷// 1. 忽略 null 檢查直接轉換
var s = (string)obj; // obj 為 null 時回傳 null,但後續 s.Length 拋出 NRE
// 2. 陣列逆變數後寫入
object[] arr = { "a" };
string[] strs = (string[])arr;
strs[0] = "b"; // 成功
arr[0] = 123; // ArrayTypeMismatchException (延遲炸彈)
// 3. 介面轉換假設實作
IEnumerable e = GetEnumerable();
IList list = (IList)e; // 可能失敗,應用 is/as
// 4. 密封類別介面轉換
sealed class SealedClass { }
ICloneable c = (ICloneable)new SealedClass(); // 編譯錯誤 (密封類別無顯式介面轉換)
精通顯式參考轉換的分類規則、執行時期檢查語意與現代替代方案,能構建更安全、高效且可維護的多型系統。