本篇文章讨论可空值类型(Nullable<T>)的转换,却确地说是如何将一种类型的值对象转换成相应的可空值。这来源于今天咱们的一个成员遇到的一个小问题,我通过一些整理写了这篇文章。虽然没有什么技术含量可言,也但愿对某些读者带来帮助。app
目录
1、四种典型的值类型转换方式
2、当类型转换遭遇Nullable<T>
3、将基于Nullable<T>的类型转换实如今扩展方法中
4、进一步完善扩展方法ConvertTo
5、谈谈NullableTypeConverterthis
对于类型转化,或者进一步地,对于像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: }
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;
从上面的介绍咱们能够得出这样的结论:若是类型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>();
上面定义的扩展方法只能完成针对目标类型为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?>();
上面谈到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);