本文探討可為 null 的實值型別(Nullable<T>)的型別轉換難題:如何將任意型別物件轉換為對應的可為 null 型別。這源自團隊成員遇到的實際問題,經整理後分享給大家。
int intValue = (int)doubleValue;int val = Convert.ToInt32("123"); 或 Convert.ChangeType(obj, typeof(int))DateTimeConverter、BooleanConverterDateTime.Parse("2024-01-01")Convert.ChangeType 無法直接轉換為 Nullable<T>:
string literalValue = "123";
try
{
int? intValue = (int?)Convert.ChangeType(literalValue, typeof(int?));
}
catch (InvalidCastException ex)
{
Console.WriteLine(ex.Message);
// 輸出:Invalid cast from 'System.String' to 'System.Nullable`1[[System.Int32...]]'
}
即使來源已是 int 也會失敗:
int intValue1 = 123;
int? intValue = (int?)Convert.ChangeType(intValue1, typeof(int?)); // 同樣拋出例外
但實際上 int 可隱式/顯式轉為 int?:
int? v1 = 123; // 隱式
int? v2 = (int?)123; // 顯式
public static class ConvertionExtensions
{
public static T? ConvertTo(this IConvertible convertibleValue) where T : struct
{
if (convertibleValue == null) return null;
return (T?)Convert.ChangeType(convertibleValue, typeof(T));
}
}
// 使用範例
int? i = "123".ConvertTo();
double? d = "123".ConvertTo();
DateTime? dt = "1981-08-24".ConvertTo();
public static T ConvertTo(this IConvertible convertibleValue)
{
if (convertibleValue == null) return default(T);
if (!typeof(T).IsGenericType)
{
return (T)Convert.ChangeType(convertibleValue, typeof(T));
}
else
{
Type genericDef = typeof(T).GetGenericTypeDefinition();
if (genericDef == typeof(Nullable<>))
{
Type underlying = Nullable.GetUnderlyingType(typeof(T));
return (T)Convert.ChangeType(convertibleValue, underlying);
}
}
throw new InvalidCastException($"Invalid cast from '{convertibleValue.GetType().FullName}' to '{typeof(T).FullName}'.");
}
// 統一介面調用
int i1 = "123".ConvertTo();
int? i2 = "123".ConvertTo();
DateTime dt1 = "1981-08-24".ConvertTo();
DateTime? dt2 = "1981-08-24".ConvertTo();
typeof(T).IsGenericType:判斷是否為泛型型別GetGenericTypeDefinition():取得泛型定義(如 Nullable<>)Nullable.GetUnderlyingType():取得 Nullable 包裝的實際型別(如 Int32).NET 內建 NullableConverter 專門處理 Nullable 型別轉換:
string literalValue = "1981-08-24";
NullableConverter converter = new NullableConverter(typeof(DateTime?));
DateTime? dateTimeValue = (DateTime?)converter.ConvertFromString(literalValue);
適用於資料繫結、屬性網格等設計時期場景。