顯式類型轉換 (強制轉型) 是開發者明確指示編譯器進行型別轉換的機制,語法為 (T)expr。它包含所有隱含轉換,並擴展至可能遺失資訊、拋出例外或需執行時期檢查的轉換。深入理解各類轉換規則與邊界條件,對編寫健壯程式碼至關重要。
(目標類型)運算式 // 語意:嘗試將運算式結果轉換為目標類型 // 可能結果:成功、資訊遺失、溢位例外、InvalidCastException、NullReferenceException
long l = (long)5; (合法但多餘)在無隱含轉換的數值類型間轉換:
| 來源 → 目標 | 範例 | 風險 |
|---|---|---|
| 較大整數 → 較小整數 | long → int, int → short, int → byte | 截斷高位、溢位 |
| 有號 → 無號 / 無號 → 有號 | int → uint, uint → int, long → ulong | 符號位重新解讀、溢位 |
| 浮點數 → 整數 | double → int, float → long, decimal → int | 截斷小數部、溢位、NaN/Infinity 處理 |
| 高精度 → 低精度浮點數 | double → float, decimal → double, decimal → float | 精度遺失、溢位為 Infinity |
| 字元 → 整數 | char → int, char → short | Unicode 碼位值、可能溢位 |
// 1. 整數 → 整數:截斷高位 (unchecked) / 例外 (checked)
int i = 300;
byte b = (byte)i; // 44 (300 % 256)
checked { byte c = (byte)i; } // OverflowException
// 2. 浮點數 → 整數:向零截斷小數部
double d = 3.9;
int i2 = (int)d; // 3
double neg = -3.9;
int i3 = (int)neg; // -3 (向零,非向下)
// 溢位:超出目標範圍 → OverflowException (checked) / 未定義 (unchecked)
double huge = 1e20;
int crash = (int)huge; // OverflowException (checked)
// NaN/Infinity → 整數
(int)double.NaN // 0 (未定義行為,實際常為 0)
(int)double.PositiveInfinity // 未定義/例外
// 3. double → float:IEEE 754 捨入最近
double precise = 1.123456789012345;
float f = (float)precise; // 1.1234568f (精度遺失)
// 4. decimal → float/double:可能溢位為 Infinity
decimal maxDec = decimal.MaxValue;
double d2 = (double)maxDec; // 7.9228162514264338E+28 (在 double 範圍內)
float f2 = (float)maxDec; // Infinity (超出 float 範圍)
// 5. float/double → decimal:精度限制 28-29 位
double pi = Math.PI;
decimal decPi = (decimal)pi; // 3.14159265358979m (精度截斷)
// 6. decimal → 整數:向零截斷
decimal dec = 10.9m;
int i4 = (int)dec; // 10
列舉與整數/其他列舉間轉換:
enum Color : int { Red = 1, Green = 2, Blue = 4 }
enum Priority { Low, Medium, High } // 底層 int
// 整數 → 列舉
Color c1 = (Color)2; // Green
Color c2 = (Color)99; // 99 (無對應成員,合法但危險)
// 列舉 → 整數
int val = (int)Color.Red; // 1
// 列舉 → 列舉 (不同底層類型需相容)
Priority p = (Priority)Color.Red; // Priority.Low (值 1)
// 底層機制:轉換為底層類型 → 目標類型
// Color (int) → int → Priority (int)
[Flags] enum Perm { Read = 1, Write = 2, Exec = 4 }
Perm p = (Perm)3; // Read | Write (值 3)
// 注意:非 Flags 列舉轉換整數無語意檢查
參考類型間、介面間、陣列共變數/逆變數轉換:
| 轉換類型 | 範例 | 執行時期檢查 |
|---|---|---|
| 衍生類別 → 基底類別/介面 | Derived → Base, Derived → IInterface | 隱含轉換 (無需顯式,但可寫) |
| 基底類別 → 衍生類別 | Base → Derived | 是 (可能 InvalidCastException) |
| 介面 → 實作類別 | IInterface → Class | 是 |
| 介面 → 介面 (非繼承) | I1 → I2 | 是 |
| 陣列共變數 | string[] → object[] | 隱含 (但寫入檢查元素型別) |
| 陣列逆變數 | object[] → string[] | 是 (執行時期檢查) |
| 委派逆變數/共變數 | Action<object> → Action<string> | 是 |
| object/介面 → 實質類別 | object → StringBuilder | 是 |
// 來源必須為 null 或可轉換為目標類型 object obj = "hello"; StringBuilder sb = (StringBuilder)obj; // InvalidCastException: 字串非 StringBuilder object obj2 = new StringBuilder(); StringBuilder sb2 = (StringBuilder)obj2; // 成功 // null 總是轉換成功 object n = null; string s = (string)n; // null (不拋出例外) // 參考轉換不改變物件身分,只改變編譯時期型別 // 同一物件,不同「視角」
// 共變數:衍生元素 → 基底元素 (唯讀安全)
string[] strings = { "a", "b" };
object[] objects = strings; // 隱含轉換,可寫 (object[])strings
// 逆變數:基底元素 → 衍生元素 (寫入危險)
object[] objs = { "a", "b" };
string[] strs = (string[])objs; // 成功 (元素確實為 string)
objs[0] = 123; // ArrayTypeMismatchException (執行時期檢查)
// 維度必須相同,元素類型皆為參考類型
// int[] → object[] 不允許 (int 為值類型)
| 語境 | 關鍵字 | 整數溢位行為 | 浮點數/decimal |
|---|---|---|---|
| 預設 | (無 / unchecked) | 環繞 (截斷高位) | IEEE 754 / 例外 |
| 檢查 | checked | OverflowException | 不影響 (浮點數無溢位概念,decimal 總檢查) |
| 常數運算式 | (編譯器) | 永遠檢查 (編譯錯誤) | 編譯時期計算 |
// 區塊範圍
checked
{
int a = int.MaxValue;
int b = a + 1; // OverflowException
}
// 運算式範圍
int c = checked(int.MaxValue + 1); // OverflowException
// 關閉檢查 (預設)
unchecked
{
int d = int.MaxValue + 1; // 環繞為 int.MinValue
}
public readonly struct Celsius
{
public double Value { get; }
// 隱含轉換:double → Celsius (安全、無遺失)
public static implicit operator Celsius(double d)
=> new Celsius { Value = d };
// 顯式轉換:Celsius → double (可能遺失單位資訊)
public static explicit operator double(Celsius c)
=> c.Value;
// 顯式轉換:Celsius → Fahrenheit (領域轉換)
public static explicit operator Fahrenheit(Celsius c)
=> new Fahrenheit { Value = c.Value * 9/5 + 32 };
}
checked — 或 TryConvert (Core 2.0+) 避免靜默資料遺失as 或 is 模式避免例外Enum.IsDefined精通顯式轉換的分類規則、溢位語境與執行時期檢查機制,能在型別安全與開發效率間取得最佳平衡。