談談Nullable的類型轉換問題

本篇文章討論可空值類型(Nullable<T>)的轉換,卻確地說是如何將一種類型的值對象轉換成相應的可空值。這來源於今天咱們的一個成員遇到的一個小問題,我通過一些整理寫了這篇文章。雖然沒有什麼技術含量可言,也但願對某些讀者帶來幫助。app

目錄 
1、四種典型的值類型轉換方式 
2、當類型轉換遭遇Nullable<T> 
3、將基於Nullable<T>的類型轉換實如今擴展方法中 
4、進一步完善擴展方法ConvertTo 
5、談談NullableTypeConverterthis

1、四種典型的類型轉換方式

對於類型轉化,或者進一步地,對於像Int、Double、DateTime、String等這些原生類型之間的轉化,咱們具備四種典型的轉換方式。若是類型之間不具備隱士轉換關係存儲,咱們能夠之間經過類型轉換操做符進行顯式轉換,好比:spa

   1: double doubleValue  = 3.14159265;
   2: int intValue        = (int)doubleValue;

第二種則是藉助於Convert這個靜態類型的ChangeType或者ToXxx方法(Xxx表明轉換的目標類型),好比:code

   1: string literalValue = "123";
   2: int intValue1       = Convert.ToInt32(literalValue);
   3: int intValue2       = (int)Convert.ChangeType(literalValue, typeof(int));

第三種方法爲建立TypeConverter或者它的基於具體類型的若干子類,好比StringConverter、BooleanConverter、DateTimeConverter等。在使用的時候你須要先實例化相應的TypeConverter,而後調用相應的類型轉換方法。好比:orm

   1: string literalValue                 = "1981-08-24";
   2: DateTimeConverter dateTypeConverter = new DateTimeConverter();
   3: DateTime dateTimeValue   = (DateTime)dateTypeConverter.ConvertFromString(literalValue);
   4:  
   5: literalValue                        = "02:40:50";
   6: TimeSpanConverter timeSpanConverter = new TimeSpanConverter();
   7: TimeSpan timeSpanValue              = (TimeSpan)timeSpanConverter.ConvertFromString(literalValue);

最後一種常見的方法用在將基於某種具體類型的格式化字符串轉化成對應的類型,咱們能夠調用具體類型的靜態方法Parse或者TryParse實現類型的轉換,好比:對象

   1: string literalValue     = "1981-08-24";
   2: DateTime dateTimeValue1 = DateTime.Parse(literalValue);
   3: DateTime dateTimeValue2;
   4: if (DateTime.TryParse(literalValue, out dateTimeValue2))
   5: { 
   6:     //...
   7: }

 

2、當類型轉換遭遇Nullable<T>類型

Convert幾乎實現全部「兼容類型」之間的轉換,也能夠向Parse方法同樣解析具備合法格式的字符串。可是,若是目標類型換成是Nullable<T>類型的時候,類型轉換將會失敗。好比咱們將上面第二個例子的目標類型從int換成int?(Nullable<Int32>):接口

   1: string literalValue = "123";
   2: try
   3: {
   4:     int? intValue = (int?)Convert.ChangeType(literalValue, typeof(int?));
   5: }
   6: catch (InvalidCastException ex)
   7: {
   8:     Console.WriteLine(ex.Message);
   9: }

類型轉換錯誤消息會被輸出:ip

   1: Invalid cast from 'System.String' to 'System.Nullable`1[[System.Int32, mscorlib,
   2:  Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]'.

實際上,若是你調用Convert的ChangeType方法將任何類型對象轉換成Nullable<T>類型,都會拋出出InvalidCastException異常,即便你將T類型轉化成Nullable<T>。好比,咱們將上面的例子中原數據類型換成int類型:字符串

   1: int intValue1 = 123;
   2: try
   3: {
   4:     int? intValue = (int?)Convert.ChangeType(intValue1, typeof(int?));
   5: }
   6: catch (InvalidCastException ex)
   7: {
   8:     Console.WriteLine(ex.Message);
   9: }

依然會輸入相似的錯誤信息:string

   1: Invalid cast from 'System.Int32' to 'System.Nullable`1[[System.Int32, mscorlib,
   2: Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]'.

而實際上,T類型的對象是能夠顯式或者隱式轉化成Nullable<T>對象的。也就是說,下面代碼所表示的類型轉換是沒有問題的:

   1: int intValue1   = 123;
   2: int? intValue2  = intValue1;
   3: int? intValue3  = (int?)intValue1;

 

3、將基於Nullable<T>的類型轉換實如今擴展方法中

從上面的介紹咱們能夠得出這樣的結論:若是類型T1和T2可以相互兼容,咱們能夠藉助Convert將T1類型對象轉換成T2類型,而後經過顯式類型轉換進一步轉換成Nullable<T2>。咱們能夠經過這兩個步驟實現針對於Nullable<T>類型的轉換。爲了操做方便,我將此轉換邏輯寫在針對IConvertible接口的擴展方法中:

   1: public static class ConvertionExtensions
   2: {
   3:     public static T? ConvertTo<T>(this IConvertible convertibleValue) where T : struct
   4:     {
   5:         if (null == convertibleValue)
   6:         {
   7:             return null;
   8:         }
   9:         return (T?)Convert.ChangeType(convertibleValue, typeof(T));
  10:     }
  11: }

藉助於上面這個擴展方法ConvertTo,對於目標類型爲Nullable<T>的轉換就顯得很簡單了:

   1: int? intValue           = "123".ConvertTo<int>();
   2: double? doubleValue     = "123".ConvertTo<double>();
   3: DateTime? dateTimeValue = "1981-08-24".ConvertTo<DateTime>();

 

4、進一步完善擴展方法ConvertTo

上面定義的擴展方法只能完成針對目標類型爲Nullable<T>的轉換。如今咱們來進一步完善它,讓這個方法能夠實現任意類型之間的轉換。下面是咱們新版本的ConvertTo方法的定義:

   1: public static T ConvertTo<T>(this IConvertible convertibleValue)
   2: {
   3:     if (null == convertibleValue)
   4:     {
   5:         return default(T);
   6:     }
   7:  
   8:     if (!typeof(T).IsGenericType)
   9:     {
  10:         return (T)Convert.ChangeType(convertibleValue, typeof(T));
  11:     }
  12:     else
  13:     {
  14:         Type genericTypeDefinition = typeof(T).GetGenericTypeDefinition();
  15:         if (genericTypeDefinition == typeof(Nullable<>))
  16:         {
  17:             return (T)Convert.ChangeType(convertibleValue, Nullable.GetUnderlyingType(typeof(T)));
  18:         }
  19:     }
  20:     throw new InvalidCastException(string.Format("Invalid cast from type \"{0}\" to type \"{1}\".", convertibleValue.GetType().FullName, typeof(T).FullName));
  21: }

在上面的方法中,咱們首先須要肯定目標類型是不是Nullable<T>,這個能夠經過調用Type對象的GetGenericTypeDefinition方法來判斷。若是是,則先要將其轉換成對應的基本類型(Nullable<T>的泛型類型)。咱們能夠經過調用靜態類Nullable的靜態方法GetUnderlyingType來得到這個基本類型(Underlying Type)。有了這個完善版本的ConvertTo擴展方法,咱們就能夠進行任意的類型轉化了——不論目標類型是可空值類型,仍是非可空值類型:

   1: int intValue1               = "123".ConvertTo<int>();
   2: int? intValue2              = "123".ConvertTo<int?>();           
   3: DateTime dateTimeValue1     = "1981-08-24".ConvertTo<DateTime>();
   4: DateTime? dateTimeValue2    = "1981-08-24".ConvertTo<DateTime?>();

 

5、談談NullableConverter

上面談到TypeConverter這個類型,而且說到它具備一系列針對具體數據類型的子類。其中一個子類就是NullableConverter,故名思義,這個TypeConverter專門用於Nullable<T>的類型轉換。使用該類實現針對可空值類型的轉換很方便,好比:

   1: string literalValue         = "1981-08-24";
   2: NullableConverter converter = new NullableConverter(typeof(DateTime?));
   3: DateTime? dateTimevalue     = (DateTime?)converter.ConvertFromString(literalValue);
相關文章
相關標籤/搜索