型別轉換 是將一種型別的值轉換為另一種型別。C# 提供隱含轉換 (安全、無資料遺失)、顯式轉換 (需轉型運算子、可能遺失資料/拋例外)、使用者定義轉換、轉換輔助類別、模式比對 等多種機制。正確選擇轉換方式能確保型別安全、避免執行期錯誤。
編譯器自動執行,保證不遺失資訊、不拋例外:
// 數值擴展
int i = 42;
long l = i; // int -> long
float f = i; // int -> float (可能精度遺失,但 C# 允許)
double d = f; // float -> double
decimal m = i; // int -> decimal
// 衍生類 -> 基底類/介面
string s = "hello";
object o = s; // 參考轉換
IEnumerable en = new List(); // 介面轉換
// 常數縮小 (編譯期已知值在目標範圍)
byte b = 100; // int 常數 -> byte
const int Small = 200;
byte b2 = Small; // OK
// int runtime = 200; byte b3 = runtime; // 錯誤:非常數需顯式
可能遺失資料或執行期失敗,開發者承擔風險:
// 數值縮小
double d = 3.14159;
int i = (int)d; // 3 (截斷小數)
long big = 5000000000;
int small = (int)big; // 截斷高位,結果不可預期
// 基底類 -> 衍生類 (下行轉換)
object obj = "hello";
string str = (string)obj; // 成功
// int num = (int)obj; // InvalidCastException
// 介面 -> 實作類
IEnumerable en = new List();
var list = (List)en; // 成功 (若實際為 List)
// 1. is 型別模式 (C# 7+)
if (obj is string s) { Console.WriteLine(s.Length); }
// 2. as 運算子 (僅參考/可為 Null 型別)
string s2 = obj as string; // 失敗回傳 null
if (s2 != null) { /* ... */ }
// 3. 模式比對 switch
var result = obj switch {
string s => s.Length,
int i => i * 2,
_ => 0
};
為自訂型別定義隱含/顯式轉換運算子:
public readonly struct Temperature {
public double Celsius { get; }
public Temperature(double c) => Celsius = c;
// 隱含轉換:double -> Temperature (安全)
public static implicit operator Temperature(double c) => new Temperature(c);
// 顯式轉換:Temperature -> double (可能遺失單位資訊)
public static explicit operator double(Temperature t) => t.Celsius;
// 另一個結構體轉換
public static implicit operator Temperature(Fahrenheit f)
=> new Temperature((f.Value - 32) * 5/9);
}
public readonly struct Fahrenheit {
public double Value { get; }
public Fahrenheit(double v) => Value = v;
}
// 使用
Temperature t = 25.0; // 隱含:double -> Temperature
double c = (double)t; // 顯式:Temperature -> double
Fahrenheit f = new(98.6);
Temperature t2 = f; // 隱含:Fahrenheit -> Temperature
public staticSystem.Convert:基礎型別轉換int i = Convert.ToInt32("42"); // 字串 -> int
double d = Convert.ToDouble("3.14"); // 字串 -> double
string s = Convert.ToString(42); // int -> 字串
bool b = Convert.ToBoolean(1); // 非零 -> true
DateTime dt = Convert.ToDateTime("2024-01-01");
// 溢位檢查:超出目標範圍拋 OverflowException
byte bb = Convert.ToByte(300); // 例外
Parse / TryParse:字串解析int i = int.Parse("42"); // 失敗拋 FormatException/OverflowException
bool ok = int.TryParse("42", out int j); // 不拋例外,回傳 bool
// 格式化選項
int hex = int.Parse("FF", NumberStyles.HexNumber); // 255
var styles = NumberStyles.AllowCurrencySymbol | NumberStyles.AllowThousands;
decimal price = decimal.Parse("$1,234.56", styles);
TypeConverter / JsonSerializer / XmlSerializer複雜物件序列化/反序列化、設計工具整合、設定綁定。
// C# 7+ is 型別模式宣告
if (input is int i) { /* i 為 int */ }
if (input is string { Length: > 10 } s) { /* s 為長字串 */ }
// switch 運算式
object Convert(object input) => input switch {
int i => i.ToString(),
double d => d.ToString("F2"),
DateTime dt => dt.ToString("o"),
IFormattable f => f.ToString(null, CultureInfo.CurrentCulture),
_ => input?.ToString() ?? ""
};
int i = 42;
object o = i; // 裝箱:配置物件、複製值
int j = (int)o; // 解箱:驗證型別、複製值
// 避免裝箱:用泛型、介面約束
void Process(T value) where T : struct { }
dynamic d = "hello";
int len = (int)d.Length; // 執行期繫結,失敗拋 RuntimeBinderException
unsafe {
int i = 42;
int* p = &i;
void* vp = p; // 隱含 void*
int* p2 = (int*)vp; // 顯式回 int*
long addr = (long)p; // 指標 -> 整數
int* p3 = (int*)addr; // 整數 -> 指標
}
is/as/switch 替代 (Type)x,避免 InvalidCastException。Span<T>、實值型別介面約束。as 可以用於實值型別嗎?A: 不能直接用於非可為 Null 實值型別。可用 int?:int? i = obj as int?;。
Convert.ToInt32 vs int.Parse 差異?A: Convert 接受 null 回傳 0,Parse 拋 ArgumentNullException。Convert 支援更多型別來源。
double -> int?A: 會遺失小數精度、可能溢位。C# 要求開發者明確承擔風險 (顯式轉換)。
A: 會。隱含轉換優先序高於顯式。模稜兩可時編譯器報錯。