一、简介

  众所周知,值类型变量不能null,这也是为什么它们被称为值类型。但是,在实际的开发过程中,也需要值为null的一些场景。例如以下场景:

  场景1:您从数据库表中检索可空的整数数据列,数据库中的null值没有办法将此值分配给C#中Int32类型;

  场景2:您在UI绑定属性,但是某些值类型的字段不是必须录入的(例如在人员管理中的死亡日期);

  场景3:在Java中,java.Util.Date是一个引用类型,因此可以将此类型的字段设置为null。但是,在CLR中,System.DateTime是一个值类型,DateTime 变量不能null。如果使用Java编写的应用程序要将日期/时间传达给在CLR上运行的Web服务,如果Java应用程序发送是null, CLR中没有供对应的类型;

  场景4:在函数中传递值类型时,如果参数的值无法提供并且不想传递,可以使用默认值。但有时默认值并不是最佳的选择,因为默认值实际也传递了一个默认的参数值,逻辑需要特殊的处理;

  场景5:当从xml或json反序列化数据时,数据源中缺少某个值类型属性的值,这种情况很不方便处理。

  当然,我们日常工作中还有很多类似的情况。

  为了摆脱这些情况,Microsoft在CLR中增加了可为空值类型的概念。为了更清楚理解这一点,我们看一下System.Nullable<T>类型的逻辑定义:

 1 namespace System 2 { 3     [Serializable] 4     public struct Nullable<T> where T : struct  5     { 6         private bool hasValue; 7         internal T value; 8   9         public Nullable(T value) {10             this.value = value;
11             this.hasValue = true;12         }13 14         public bool HasValue {
15             get {16                 return hasValue;
17             }
18         }19  20         public T Value {
21             get {22                 if (!HasValue) {
23                     ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NoValue);
24                 }25                 return value;
26             }27         }28 29         public T GetValueOrDefault() {
30             return value;31         }
32 33         public T GetValueOrDefault(T defaultValue) {34             return HasValue ? value : defaultValue;35         }
36 37         public override bool Equals(object other) {
38             if (!HasValue) return other == null;
39             if (other == null) return false;40             return value.Equals(other);
41         }42 43         public override int GetHashCode() {44             return HasValue ? value.GetHashCode() : 0;
45         }46  47         public override string ToString() {
48             return HasValue ? value.ToString() : "";49         }
50 51         public static implicit operator Nullable<T>(T value) {52             return new Nullable<T>(value);53         }
54 55         public static explicit operator T(Nullable<T> value) {
56             return value.Value;
57         }58     }59 }

  从上面的定义可以总结如下几点:

  • Nullable<T> 类型也是一个值类型;

  • Nullable<T> 类型包含一个Value属性用于表示基础值,还包括一个Boolean类型的HasValue属性用于表示该值是否为null ;

  • Nullable<T> 是一个轻量级的值类型。Nullable<T>类型的实例占用内存的大小等于一个值类型与一个Boolean类型占用内存大小之和;

  • Nullable<T> 的泛型参数T必须是值类型。您只能将Nullable<T>类型与值类型结合使用,您也可以使用用户定义的值类型。

二、语法和用法

  使用Nullable<T>类型,只需指定一个其它值类型的泛型参数T。

  示例: 

1     Nullable<int> i = 1;2     Nullable<int> j = null;3     Nullable<Nullable<int>> k; //这是一个错误语法,编译会报错。

  CLR还提供了一种简写的方式。

1     int? i = 1;2     int? j = null;

  可以通过 Value 属性来获取基础类型的值。如下所示,如果不为null,则将返回实际的值,否则将抛出InvalidOperationException异常;您可以在调用Value属性的时,需要检查是否为null。

 1     Nullable<int> i = 1; 2     Nullable<int> j = null; 3  4     Console.WriteLine(i.HasValue); 5     //输出结果:True 6  7     Console.WriteLine(i.Value); 8     //输出结果:1 9 10     Console.WriteLine(j.HasValue);11     //输出结果:False12 13     Console.WriteLine(j.Value);14     //抛异常: System.InvalidOperationException

三、类型的转换和运算

  C#还支持简单的语法来使用Nullable<T>类型。它还支持Nullable<T>实例的隐式转换和转换。如下示例演示:

 1     // 从System.Int32隐式转换为Nullable<Int32>  2     int? i = 5; 3  4     // 从'null'隐式转换为Nullable<Int32>  5     int? j = null; 6  7     // 从Nullable<Int32>到Int32的显式转换 8     int k = (int)i; 9 10     // 基础类型之间的转换11     Double? x = 5; // 从Int到Nullable<Double> 的隐式转换12     Double? y = j; // 从Nullable<Int32> 隐式转换Nullable<Double>

  对Nullable<T> 类型使用操作符,与包含的基础类型使用方法相同。

  • 一元运算符(++、--、 - 等),如果Nullable<T>类型值是null时,返回null;

  • 二元运算符(+、-、*、/、%、^等)任何操作数是null,返回null;

  • 对于==运算符,如果两个操作数都是null,则表达式计算结果为true,如果任何一个操作数是null,则表达式计算结果为false;如果两者都不为null,它照常比较。

  • 对于关系运算符(>、<、>=、<=),如果任何一个操作数是null,则运算结果是false,如果操作数都不为null,则比较该值。

  见下面的例子:  

 1     int? i = 5; 2     int? j = null; 3  4     // 一元运算符 5     i++; // i = 6  6     j = -j; // j = null 7  8     // 二元运算符 9     i = i + 3; // i = 9 10     j = j * 3; // j = null;11 12     // 等号运算符(==、!=)13     var r = i == null; //r = false14     r = j == null; //r = true15     r = i != j; //r = true16 17     // 比较运算符(<、>、<=、>=)18     r = i > j; //r = false19 20     i = null;21     r = i >= j; //r = false,注意,i=null、j=null,但是>=返回的结果是false

  Nullable<T>也可以像引用类型一样,支持三元操作符。

1     // 如果雇员的年龄返回null(出生日期可能未输入),请设置值0. 2     int age = employee.Age ?? 0;3 4     // 在聚合函数中使用三元操作符。5     int?[] numbers = {};6     int total = numbers.Sum() ?? 0;

四、装箱与拆箱

  我们已经知道了Nullable<T>是一个值类型,现在我们再来聊一聊它的装箱与拆箱。
CLR采用一个特殊的规则来处理Nullable<T>类型的装箱与拆箱。当一个Nullable<T>类型的实例装箱时,CLR会检查实例的HasValue属性:如果是true,则将实例Value属性的值进行装箱后返回结果;如果返回false,则直接返回null,不做任何的处理。
在拆箱处理时,与装箱处反。CLR会检查拆箱的对象是否为null,如果是直接创建一个新的实例 new Nullable<T>(),如果不为null,则将对象拆箱为类型T,然后创建一个新实例 new Nullable<T>(t)。

 1     int? n = null; 2     object o = n; //不会进行装箱操作,直接返回null值 3  4     Console.WriteLine("o is null = {0}", object.ReferenceEquals(o, null)); 5     //输出结果:o is null = True 6  7  8     n = 5; 9     o = n; //o引用一个已装箱的Int3210 11     Console.WriteLine("o's type = {0}", o.GetType());12     //输出结果:o's type = System.Int3213 14     o = 5;15 16     //将Int32类型拆箱为Nullable<Int32>类型17     int? a = (Int32?)o; // a = 5
18     //将Int32类型拆箱为Int32类型19     int b = (Int32)o; // b = 520 21     // 创建一个初始化为null22     o = null;23     // 将null变为Nullable<Int32>类型24     a = (Int32?)o; // a = null 25     b = (Int32)o; // 抛出异常:NullReferenceException

五、GetType()方法

  当调用Nullable<T>类型的GetType()方法时,CLR实际返回类型的是泛型参数的类型。因此,您可能无法区分Nullable<Int32>实例上是一个Int32类型还是Nullable<Int32>。见下面的例子:

1     int? i = 10;2     Console.WriteLine(i.GetType());3     //输出结果是:System.Int324     5     i = null;6     Console.WriteLine(i.GetType()); //NullReferenceException

  原因分析:

  这是因为调用GetType()方法时,已经将当前实例进行了装箱,根据上一部分装箱与拆箱的内容,这里实际上调用的是Int32类型的GetType()方法。

  调用值类型的GetType()方法时,均会产生装箱,关于这一点大家可以自己去验证。

六、ToString()方法

  当调用Nullable<T>类型的ToString()方法时,如果HasValue属性的值为false,则返回String.Empty,如果该属性的值为true,则调用的逻辑是Value.ToString()。 见下面的例子:

1     int? i = 10;2     Console.WriteLine(i.ToString());3     //输出结果:104 5     i = null;6     Console.WriteLine(i.ToString() == string.Empty);7     //输出结果:True

七、System.Nullable帮助类

  微软还提供一个同名System.Nullable的静态类,包括三个方法:

 1 public static class Nullable 2 { 3     //返回指定的可空类型的基础类型参数。 4     public static Type GetUnderlyingType(Type nullableType); 5  6     //比较两个相对值 System.Nullable<T> 对象。 7     public static int Compare<T>(T? n1, T? n2) where T : struct 8  9     //指示两个指定 System.Nullable<T> 对象是否相等。10     public static bool Equals<T>(T? n1, T? n2) where T : struct11 }

  在这里面我们重点说明一下GetUnderlyingType(Type nullableType)方法,另外两个方法是用来比较值的,大家可以自己研究。

  GetUnderlyingType(Type nullableType)方法是用来返回一个可为空类型的基础类型,如果 nullableType 参数不是一个封闭的Nullable<T>泛型,则反回null。

 1     Console.WriteLine(Nullable.GetUnderlyingType(typeof(Nullable<int>))); 2     //输出结果:System.Int32 3  4     Console.WriteLine(Nullable.GetUnderlyingType(typeof(Nullable<>)) == null); 5     //输出结果:True 6  7     Console.WriteLine(Nullable.GetUnderlyingType(typeof(int)) == null); 8     //输出结果:True 9 10     Console.WriteLine(Nullable.GetUnderlyingType(typeof(string)) == null);11     //输出结果:True

八、语法糖

  微软对Nullable<T>提供了丰富的语法糖来减少开发员的工作量,下面是我想到供您参考。

简写 编译后的语句

 1     int? i = 5; 2  3     int? j = null; 4  5     var r = i != null; 6  7     var v = (int) i; 8  9     i++;10 11     i = i + 3;12 13     r = i != j;14 15     r = i >= j;16 17     var k = i + j;18 19     double? x = 5;20     21     double? y = j;

 1     int? i = new int?(5); 2  3     int? j = new int?(); 4  5     var r = i.HasValue; 6  7     var v = i.Value; 8  9     i = i.HasValue ? new int?(i.GetValueOrDefault() + 1) : new int?();10 11     i = i.HasValue ? new int?(i.GetValueOrDefault() + 3) : new int?();12 13     r = i.GetValueOrDefault() != j.GetValueOrDefault() || i.HasValue != j.HasValue;14 15     r = i.GetValueOrDefault() >= j.GetValueOrDefault() && i.HasValue & j.HasValue;16 17     int? k = i.HasValue & j.HasValue ? new int?(i.GetValueOrDefault() + j.GetValueOrDefault()) : new int?();18 19     double? x = new double?((double) 5);20     21     double? y = j.HasValue ? new double?((double) j.GetValueOrDefault()) : new double?();
      本文转自zsdnr  51CTO博客,原文链接:http://blog.51cto.com/12942149/1944613,如需转载请自行联系原作者

NullableT类型相关推荐

  1. 细说NullableT类型

    细说Nullable<T>类型 目录 一.简介 二.语法和用法 三.类型的转换和运算 四.装箱与拆箱 五.GetType()方法 六.ToString()方法 七.System.Nulla ...

  2. 《疯狂Java讲义》(第5版) 作者李刚(待重新排版)

    第1章 Java语言概述与开发环境 1.1 Java语言的发展简史 JDK1.0 : Sun在1996年年初发布了JDK 1.0,该版本包括两部分:运行环境(即JRE)和开发环境(即JDK).运行环境 ...

  3. 可空类型NullableT小结

    1 可空类型的声明 public struct Nullable<T> where T: struct T为结构体,所以只有值类型才可以使用Nullable<T>声明为&quo ...

  4. factorybean 代理类不能按照类型注入_Spring拓展接口之FactoryBean,我们来看看其源码实现...

    是什么 FactoryBean的源码比较简单,大家可以细读下其注释,我做了简单的如下翻译 /*** 实现此接口的bean不能用作普通bean.此bean暴露的对象是通过getObject()创建的对象 ...

  5. Spring MVC自定义类型转换器Converter、参数解析器HandlerMethodArgumentResolver

    文章目录 一.前言 二.类型转换器Converter 1.自定义类型转换器 三.参数解析器 1.自定义分页参数解析器 2.自定义注解参数解析器 一.前言 Spring MVC源码分析相关文章已出: S ...

  6. Oracle根据日期区间查询Date类型的数据

    在Oracle数据库中,根据日期区间查询Date类型的数据 select proposalno,policyno,enddate from 表名 where 时间字段 between to_date( ...

  7. Oracle type (自定义类型的使用)

    oracle - type type定义: oracle中自定义数据类型 oracle中有基本的数据类型,如number,varchar2,date,numeric,float....但有时候我们需要 ...

  8. c语言long int表示范围_C语言编程第9讲——这些C语言整数类型的知识点你掌握了吗...

    1.char其实是一种整数类型 在C语言中,字符使用整数来表示. 例如下面的代码: #include int main(){ char letter = '2'; printf("Lette ...

  9. Redis 笔记(07)— sorted set 类型(添加、删除有序集合元素、获取分数范围内成员、按score排序、返回集合元素个数)

    zset 可能是 Redis 提供的最为特色的数据结构,一方面它是一个 set,保证了内部 value 的唯一性,另一方面它可以给每个 value 赋予一个 score,代表这个 value 的排序权 ...

最新文章

  1. matlab大作业题题单,2011MATLAB大作业-题目-
  2. 解决数据库Can't connect to MySQL server on 'localhost' (10061)的问题
  3. Solve one floodlight install problem
  4. linux虚拟机lvm扩容,LVM扩容-----给虚拟机添加空间
  5. 一步步实现SDDC-分布式交换机入门
  6. ssm上传文件获取路径_ssm框架实现图片上传显示并保存地址到数据库(示例代码)...
  7. 小量数据和海量数据分页显示存储过程
  8. 赢得黑客马拉松的 9 种方式
  9. houdini 做选点效果
  10. UITableViewCell高度自适应变化
  11. Python3 循环删除列表中的指定变量
  12. Eclipse开发struts完全指南(二)安装与配置
  13. 微信小程序教程笔记5
  14. ETH-trunk(链路聚合协议)lacp
  15. 音视频转换器哪个好?嗨格式视频转换器来了
  16. html的excel表格自动换行,excel自动换行总结
  17. 基于MODBUS通讯协议的酒厂温湿度、光照度数据采集系统的设计与实现
  18. python输出间隔符_python print 使用分隔符 或行尾符
  19. 微信开放JS SDK,再次给浏览器们上了一课
  20. 2014春招CVTE面试经历

热门文章

  1. 译-使用Scroll Snapping实现CSS控制页面滚动
  2. python接口自动化(三十二)--Python发送邮件(常见四种邮件内容)番外篇——上
  3. 计算机专业跨考学科英语难吗,跨考学科英语,过来人走过的弯路
  4. python for语句_从零开始py个thon3:循环语句(1)
  5. Cookie字符串转Map集合方法
  6. win10开机,内存占用过高
  7. SQL Server 使用Detach和Attach 方式 移动数据库位置
  8. 保密作战--在网络上隐藏自己
  9. linux查看消息队列的状态,linux – 如何知道某个时间点在消息队列中收到的消息数...
  10. java 弹幕游戏_JAVA 弹幕小游戏 1.0版本