本篇文章讨论可空值类型(Nullable
目录
一、四种典型的值类型转换方式
二、当类型转换遭遇Nullable
三、将基于Nullable的类型转换实现在扩展方法中
四、进一步完善扩展方法ConvertTo
五、谈谈NullableTypeConverter
一、四种典型的类型转换方式
对于类型转化,或者进一步地,对于像Int、Double、DateTime、String等这些原生类型之间的转化,我们具有四种典型的转换方式。如果类型之间不具有隐士转换关系存储,我们可以之间通过类型转换操作符进行显式转换,比如:
1: double doubleValue = 3.14159265;
2: int intValue = (int)doubleValue;
第二种则是借助于Convert这个静态类型的ChangeType或者ToXxx方法(Xxx代表转换的目标类型),比如:
1: string literalValue = "123";
2: int intValue1 = Convert.ToInt32(literalValue);
3: int intValue2 = (int)Convert.ChangeType(literalValue, typeof(int));
第三种方法为创建TypeConverter或者它的基于具体类型的若干子类,比如StringConverter、BooleanConverter、DateTimeConverter等。在使用的时候你需要先实例化相应的TypeConverter,然后调用相应的类型转换方法。比如:
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: }
二、当类型转换遭遇Nullable类型
Convert几乎实现所有“兼容类型”之间的转换,也可以向Parse方法一样解析具有合法格式的字符串。但是,如果目标类型换成是Nullable
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: }
类型转换错误消息会被输出:
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
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: }
依然会输入类似的错误信息:
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
1: int intValue1 = 123;
2: int? intValue2 = intValue1;
3: int? intValue3 = (int?)intValue1;
三、将基于Nullable的类型转换实现在扩展方法中
从上面的介绍我们可以得出这样的结论:如果类型T1和T2能够相互兼容,我们可以借助Convert将T1类型对象转换成T2类型,然后通过显式类型转换进一步转换成Nullable
1: public static class ConvertionExtensions
2: {
3: public static T? ConvertTo(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
1: int? intValue = "123".ConvertTo();
2: double? doubleValue = "123".ConvertTo();
3: DateTime? dateTimeValue = "1981-08-24".ConvertTo();
四、进一步完善扩展方法ConvertTo
上面定义的扩展方法只能完成针对目标类型为Nullable
1: public static T ConvertTo(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
1: int intValue1 = "123".ConvertTo();
2: int? intValue2 = "123".ConvertTo();
3: DateTime dateTimeValue1 = "1981-08-24".ConvertTo();
4: DateTime? dateTimeValue2 = "1981-08-24".ConvertTo();
五、谈谈NullableConverter
上面谈到TypeConverter这个类型,并且说到它具有一系列针对具体数据类型的子类。其中一个子类就是NullableConverter,故名思义,这个TypeConverter专门用于Nullable
1: string literalValue = "1981-08-24";
2: NullableConverter converter = new NullableConverter(typeof(DateTime?));
3: DateTime? dateTimevalue = (DateTime?)converter.ConvertFromString(literalValue);