谈谈Nullable<T>的类型转换问题
本篇文章讨论可空值类型(Nullable<T>)的转换,却确地说是如何将一种类型的值对象转换成相应的可空值。这来源于今天我们的一个成员遇到的一个小问题,我经过一些整理写了这篇文章。虽然没有什么技术含量可言,也希望对某些读者带来帮助。
目录
一、四种典型的值类型转换方式
二、当类型转换遭遇Nullable<T>
三、将基于Nullable<T>的类型转换实现在扩展方法中
四、进一步完善扩展方法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<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: }
类型转换错误消息会被输出:
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: }
依然会输入类似的错误信息:
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;
三、将基于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>();
四、进一步完善扩展方法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?>();
五、谈谈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);
谈谈Nullable<T>的类型转换问题的更多相关文章
- 将基于Nullable<T>的类型转换实现在扩展方法中
三.将基于Nullable<T>的类型转换实现在扩展方法中 从上面的介绍我们可以得出这样的结论:如果类型T1和T2能够相互兼容,我们可以借助Convert将T1类型对象转换成T2类型,然后 ...
- 谈谈Delph中的类和对象2---类可以理解成一种特殊的数据结构、类型转换
三.类可以理解成一种特殊的数据结构 我们知道数据类型可以进行强制类型转换,类既然可以理解成一种数据类型,那么它也应该可以进行类型转换.比如下面代码为一个按钮(Button1)的单击事件 procedu ...
- 四、可空类型Nullable<T>到底是什么鬼
值类型为什么不可以为空 首先我们都知道引用类型默认值都是null,而值类型的默认值都有非null. 为什么引用类型可以为空?因为引用类型变量都是保存一个对象的地址引用(就像一个url对应一个页面),而 ...
- 可空类型(Nullable<T>)及其引出的关于explicit、implicit的使用
问题一:Nullable<T>可赋值为null 先看两行C#代码 int? i1 = null; int? i2 = new int?(); int? 即Nullable<int&g ...
- Android 谈谈封装那些事 --BaseActivity 和 BaseFragment(二)
1.前言 昨天谈了BaseActivity的封装,Android谈谈封装那些事--BaseActivity和BaseFragment(一)有很多小伙伴提了很多建议,比如: 通用标题栏可以自定义Vi ...
- Util应用程序框架公共操作类(二):数据类型转换公共操作类(源码篇)
上一篇介绍了数据类型转换的一些情况,可以看出,如果不进行封装,有可能导致比较混乱的代码.本文通过TDD方式把数据类型转换公共操作类开发出来,并提供源码下载. 我们在 应用程序框架实战十一:创建VS解决 ...
- 【C#】可空类型(Nullable)
C# 可空类型(Nullable) C# 提供了一个特殊的数据类型,nullable 类型(可空类型),可空类型可以表示其基础值类型正常范围内的值,再加上一个 null 值. 例如,Nullable& ...
- 一直都在说反射很有用 谈谈大型.NET ERP系统有哪些地方用到了反射
反射Reflection,MFC时代叫RTTI(Runtime Type Identification) 运行时类型识别,提供一种动态创建对象的能力. 这里不谈反射的概念和基本用法,仅仅就我遇到的ER ...
- .Net下一个类型转换神器
引言 类型转换经常遇到,最常用的应该是string类型转换为其它基元类型,常见于http参数类型转换.Convert静态类的Convert.ChangeType()方法可以把实现IConvertibl ...
随机推荐
- delphi 取得汉字的第一个字母
功能说明://取得汉字的第一个字母 function GetPYIndexChar( hzchar:string):char;begin caseWORD(hzchar[1])shl8+WORD(h ...
- Android无法生成R文件的终极解决办法
R文件如果在clean项目(Project—>Clean)和 Fix Project Properties(如下图): 如果在第一步无法解决的的时候,那可能原因就是资源文件调用的错误,比如资 ...
- mysqlpump 原理
Oracle官方多线程逻辑备份工具 昨天Inside君写到MySQL 5.7.11版本发布,其中最有意义的部分在于官方修复了之前mysqlpump工具一致性备份的问题,使得mysqlpump工具在生产 ...
- 如何制作按钮hover状态
1.选中文字加背景图层 2.Ctrl+J复制图层,向下轻移. 3.点击所复制图层的背景图层,右键选中——混合选项 弹出图层样式框: 4.选择渐变叠加,将渐变——反向勾选上,确定: 5.完成,效果如图.
- Jfinal学习笔记
实习期间,公司使用jfinal做开发,所以就学习了这个框架 Jfinal作为一个极速ORM,使用上非常方便,核心源代码1万多行,压缩后只有200多KB. 从入口开始 作为一个web项目,首先解读web ...
- SQL Server 通配符 Wildcard character
SQL Server 通配符 Wildcard character % 任意长度的字符串,如:'%computer%' _ 单个字符,如:'_ean' [] ...
- 【安卓面试题】在一个Activity启动另一个Activity和在Service中启动一个Activity有什么区别
在Activity中可以直接使用Intent启动另一个Activity 显式Intent intent = new Intent(context, activity.class) 隐式 Intent ...
- MAC下查看端口占用并杀死进程
Eclipse在Run on Server时,Tomcat是开启的,但是报错,显示8080.8005和8009端口被占用 终端输入 查看所有开启的端口 sudo lsof -i -P | grep - ...
- python django第二弹
每天晚上应该就这样坐着,然后把每天的东西做个总结,或大或小,有的人可能愿意把自己的东西保留在草稿箱,想想我还是把他写出来吧,前几次我发现又遇到了之前遇到的简单的问题,翻看自己之前写的几篇小日记,可以很 ...
- [转]MVC自定义数据验证(两个时间的比较)
本文转自:http://www.cnblogs.com/zhangliangzlee/archive/2012/07/26/2610071.html Model: public class Model ...