谈谈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>的类型转换问题

时间: 2024-10-05 03:17:50

谈谈Nullable<T>的类型转换问题的相关文章

谈谈 MySQL 隐式类型转换

前言 今天我们继续回到MySQL系列文章中,谈一谈MySQL中隐式类型转换.(其实我最早知道是在慢SQL优化中知道隐式类型转换概念的),在说隐式类型转换之前,首先我们通过一个实例来看看是怎么回事. 数据结构 本文中所有的操作,都是基于该数据结构(有兴趣的童鞋,可以实验): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 create table t_base_user( oid bigint(20) not null primary key auto_incr

More Effective C++

条款一:指针与引用的区别 指针与引用看上去完全不同(指针用操作符'*'和'->',引用使用操作符'.'),但是它们似乎有相同的功能.指针与引用都是让你间接引用其他对象.你如何决定在什么时候使用指针,在什么时候使用引用呢? 首先,要认识到在任何情况下都不能用指向空值的引用.一个引用必须总是指向某些对象.因此如果你使用一个变量并让它指向一个对象,但是该变量在某些时候也可能不指向任何对象,这时你应该把变量声明为指针,因为这样你可以赋空值给该变量.相反,如果变量肯定指向一个对象,例如你的设计不允许变量为

谈谈Delphi的类和对象:三、类可以理解成一种特殊的数据结构、类型转换

三.类可以理解成一种特殊的数据结构 我们知道数据类型可以进行强制类型转换,类既然可以理解成一种数据类型,那么它也应该可以进行类型转换.比如下面代码为一个按钮(Button1)的单击事件 procedure TForm1.Button1Click(Sender: TObject); var ACaption: String; begin ACaption:= TButton(Sender).Caption; //Sender从TObject转化为TButton ShowMessage(Format

谈谈dynamic关键字

谈谈dynamic关键字 前言 今天和谈了两年的女朋友分手了,一开始我还觉得是因为这次的吵架,因为我今天一天没联系她,她就生气了,说了分手,我是说一开始我以为是这样.然后我想了想,矛盾就像不停的在往一个瓶子里到硫酸,有可能因为这一滴的缘故导致瓶子里的硫酸溢出来了,有了矛盾就要解决了,珍惜眼前人. 正文 废话说了一大堆,说点正经的,C#4.0提供了一个关键字dynamic,我在以前的时候说过这个关键字.今天来看看dynamic到底是什么.它是怎么工作的呢? 先来看一个简单的案例: static v

【C#】可空类型(Nullable)

C# 可空类型(Nullable) C# 提供了一个特殊的数据类型,nullable 类型(可空类型),可空类型可以表示其基础值类型正常范围内的值,再加上一个 null 值. 例如,Nullable< Int32 >,读作"可空的 Int32",可以被赋值为 -2,147,483,648 到 2,147,483,647 之间的任意值,也可以被赋值为 null 值.类似的,Nullable< bool > 变量可以被赋值为 true 或 false 或 null.

谈谈Oracle基本操作(上)

当前我们市面上流行的数据库有sybase,Oracle,DB2,Mysql,sqlSever,MSQL, MariaDB 今天我们主要谈谈Oracle数据库 一:Oracle原理 1:什么是数据库:数据库管理的是硬盘中的数据,把数据打包,方便管理,数据增删改查,它的保存数据的是数据库文件;数据库一切都是表:用数据表来保存数据; 2:分类: (1)结构化数据库:保存在硬盘当中,SQL语句查询;以SQL语句模型为基础的数据库 (2)非结构化数据库:mongoDB,Redis,memecache;把数

使用 IL 实现类型转换

在之前的文章中,我大致介绍过一些类型间的隐式和显式类型转换规则.但当时并未很仔细的研究过<CSharp Language Specification>,因此实现并不完整.而且只部分解决了类型间能否进行类型转换,仍未解决到底该如何进行类型转换,尤其是在定义泛型类型时,我们明明知道泛型类型的参数是什么类型,但就是不能直接进行类型转换: if (typeof(T) == typeof(int)) { int intValue = (int)value; // 错误:无法将类型“T”转换为“int”

可空类型(Nullable&lt;T&gt;)及其引出的关于explicit、implicit的使用

问题一:Nullable<T>可赋值为null 先看两行C#代码 int? i1 = null; int? i2 = new int?(); int? 即Nullable<int>,就像int之于Int32: Nullable<T>是非常特殊结构类型,它可赋值为null(所以此前我还以为是引用类型),其本质是等同于new: 通过调试可发现上述两个值均为null,但是事实上我们却可以调用他们的一些属性方法比如“HasValue”,由此可见“=null“只是障眼法罢了: 此

题外话:谈谈malloc()和free()

对于串的顺序存储,有些需要补充说明.串值的存储空间可在程序执行过程中动态分配而得.比如在计算机中存在一个自由存储区,叫做“堆”.这个堆可由C语言的动态分配函数malloc()和free()来管理. 那么今天就来点题外话,谈谈malloc()和free()威尼斯人赌场 malloc()和free()的基本概念以及基本用法 1. 函数原型及说明: void *malloc(long NumBytes):该函数分配了NumBytes个字节,并返回了指向这块内存的指针.如果分配失败,则返回一个空指针(N