目录
一、简介
二、语法和用法
三、类型的转换和运算
四、装箱与拆箱
五、GetType()方法
六、ToString()方法
七、System.Nullable帮助类
八、语法糖

一、简介

  众所周知,值类型变量不能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>类型的逻辑定义:

 namespace System
{
[Serializable]
public struct Nullable<T> where T : struct
{
private bool hasValue;
internal T value; public Nullable(T value) {
this.value = value;
this.hasValue = true;
} public bool HasValue {
get {
return hasValue;
}
} public T Value {
get {
if (!HasValue) {
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NoValue);
}
return value;
}
} public T GetValueOrDefault() {
return value;
} public T GetValueOrDefault(T defaultValue) {
return HasValue ? value : defaultValue;
} public override bool Equals(object other) {
if (!HasValue) return other == null;
if (other == null) return false;
return value.Equals(other);
} public override int GetHashCode() {
return HasValue ? value.GetHashCode() : ;
} public override string ToString() {
return HasValue ? value.ToString() : "";
} public static implicit operator Nullable<T>(T value) {
return new Nullable<T>(value);
} public static explicit operator T(Nullable<T> value) {
return value.Value;
}
}
}

查看Nullable的定义

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

  • Nullable<T> 类型也是一个值类型;
  • Nullable<T> 类型包含一个Value属性用于表示基础值,还包括一个Boolean类型的HasValue属性用于表示该值是否为null
  • Nullable<T> 是一个轻量级的值类型。Nullable<T>类型的实例占用内存的大小等于一个值类型与一个Boolean类型占用内存大小之和;
  • Nullable<T> 的泛型参数T必须是值类型。您只能将Nullable<T>类型与值类型结合使用,您也可以使用用户定义的值类型。

二、语法和用法

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

  示例: 

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

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

     int? i = ;
int? j = null;

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

     Nullable<int> i = ;
Nullable<int> j = null; Console.WriteLine(i.HasValue);
//输出结果:True Console.WriteLine(i.Value);
//输出结果:1 Console.WriteLine(j.HasValue);
//输出结果:False Console.WriteLine(j.Value);
//抛异常: System.InvalidOperationException

三、类型的转换和运算

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

     // 从System.Int32隐式转换为Nullable<Int32>
int? i = ; // 从'null'隐式转换为Nullable<Int32>
int? j = null; // 从Nullable<Int32>到Int32的显式转换
int k = (int)i; // 基础类型之间的转换
Double? x = ; // 从Int到Nullable<Double> 的隐式转换
Double? y = j; // 从Nullable<Int32> 隐式转换Nullable<Double>

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

  • 一元运算符(++、--、 - 等),如果Nullable<T>类型值是null时,返回null
  • 二元运算符(+、-、*、/、%、^等)任何操作数是null,返回null
  • 对于==运算符,如果两个操作数都是null,则表达式计算结果为true,如果任何一个操作数是null,则表达式计算结果为false;如果两者都不为null,它照常比较。
  • 对于关系运算符(>、<、>=、<=),如果任何一个操作数是null,则运算结果是false,如果操作数都不为null,则比较该值。

  见下面的例子:  

     int? i = ;
int? j = null; // 一元运算符
i++; // i = 6
j = -j; // j = null // 二元运算符
i = i + ; // i = 9
j = j * ; // j = null; // 等号运算符(==、!=)
var r = i == null; //r = false
r = j == null; //r = true
r = i != j; //r = true // 比较运算符(<、>、<=、>=)
r = i > j; //r = false i = null;
r = i >= j; //r = false,注意,i=null、j=null,但是>=返回的结果是false

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

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

四、装箱与拆箱

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

     int? n = null;
object o = n; //不会进行装箱操作,直接返回null值 Console.WriteLine("o is null = {0}", object.ReferenceEquals(o, null));
//输出结果:o is null = True n = ;
o = n; //o引用一个已装箱的Int32 Console.WriteLine("o's type = {0}", o.GetType());
//输出结果:o's type = System.Int32 o = ; //将Int32类型拆箱为Nullable<Int32>类型
int? a = (Int32?)o; // a = 5
//将Int32类型拆箱为Int32类型
int b = (Int32)o; // b = 5 // 创建一个初始化为null
o = null;
// 将null变为Nullable<Int32>类型
a = (Int32?)o; // a = null
b = (Int32)o; // 抛出异常:NullReferenceException

五、GetType()方法

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

     int? i = ;
Console.WriteLine(i.GetType());
//输出结果是:System.Int32 i = null;
Console.WriteLine(i.GetType()); //NullReferenceException

  原因分析:

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

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

六、ToString()方法

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

     int? i = ;
Console.WriteLine(i.ToString());
//输出结果:10 i = null;
Console.WriteLine(i.ToString() == string.Empty);
//输出结果:True

七、System.Nullable帮助类

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

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

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

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

     Console.WriteLine(Nullable.GetUnderlyingType(typeof(Nullable<int>)));
//输出结果:System.Int32 Console.WriteLine(Nullable.GetUnderlyingType(typeof(Nullable<>)) == null);
//输出结果:True Console.WriteLine(Nullable.GetUnderlyingType(typeof(int)) == null);
//输出结果:True Console.WriteLine(Nullable.GetUnderlyingType(typeof(string)) == null);
//输出结果:True

八、语法糖

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

简写 编译后的语句
     int? i = ;

     int? j = null;

     var r = i != null;

     var v = (int) i;

     i++;

     i = i + ;

     r = i != j;

     r = i >= j;

     var k = i + j;

     double? x = ;

     double? y = j;
     int? i = new int?();

     int? j = new int?();

     var r = i.HasValue;

     var v = i.Value;

     i = i.HasValue ? new int?(i.GetValueOrDefault() + ) : new int?();

     i = i.HasValue ? new int?(i.GetValueOrDefault() + ) : new int?();

     r = i.GetValueOrDefault() != j.GetValueOrDefault() || i.HasValue != j.HasValue;

     r = i.GetValueOrDefault() >= j.GetValueOrDefault() && i.HasValue & j.HasValue;

     int? k = i.HasValue & j.HasValue ? new int?(i.GetValueOrDefault() + j.GetValueOrDefault()) : new int?();

     double? x = new double?((double) );

     double? y = j.HasValue ? new double?((double) j.GetValueOrDefault()) : new double?();
 

参考:

转载请注明出处,原文链接:http://www.cnblogs.com/tdfblog/p/Nullable-Types-in-Csharp-Net.html

细说Nullable<T>类型的更多相关文章

  1. 一个高性能的对象属性复制类,支持不同类型对象间复制,支持Nullable<T>类型属性

    由于在实际应用中,需要对大量的对象属性进行复制,原来的方法是通过反射实现,在量大了以后,反射的性能问题就凸显出来了,必须用Emit来实现. 搜了一圈代码,没发现适合的,要么只能在相同类型对象间复制,要 ...

  2. 细说可空类型 nullable PropertyType

    可空类型是System.Nullable结构体的实列.一个可空类型代表了相应值类型的正确范围附加null值.这么说来,其实也不是很明子,命题嘛,一般不求易懂,但求准确. 那我就来说说这可空类型吧,上次 ...

  3. Asp.net MVC Checkbox控件 和 Nullable<bool>, 或bool?类型

    @Html.CheckBoxFor() 这个方法生成两个Input HTML标签,不明白为什么这样,如果数据库是Nullable<bool>类型,就会报错. 网上的解决方法是这样: 方法一 ...

  4. Nullable<T> 结构 |T? 可为空的值类型

    参考链接:https://www.cnblogs.com/tdfblog/p/Nullable-Types-in-Csharp-Net.html https://www.cnblogs.com/min ...

  5. 【C#进阶系列】19 可空值类型

    可空值类型,正如字面意义上的,是可以为NULL的值类型. 这个东西存在的意义可以解决比如数据库的的Int可以为NUll的情况,使得处理数据库数据更简单. 实际上可空值类型就是Nullable<T ...

  6. CLR via C#(14)-可空值类型,关于?和??的故事

    我们都知道,值类型是不能为Null的,但是在实际应用中有些情形却需要将值类型置为null.因此,CLR中引用了可空值类型的用法.今天的文章中见到最多的符号估计就是?了吧. ?——初识可空值类型 1.  ...

  7. [读书笔记]C#学习笔记四: C#2.0泛型 可控类型 匿名方法和迭代器

    前言 C#1.0的委托特性使方法作为其他方法的参数来传递,而C#2.0 中提出的泛型特性则使类型可以被参数化,从而不必再为不同的类型提供特殊版本的实现方法.另外C#2.0还提出了可空类型,匿名方法和迭 ...

  8. [CLR via C#]19. 可空值类型

    我们知道,一个值类型的变量永远不可能为null.它总是包含值类型本身.遗憾的是,这在某些情况下会成为问题.例如,设计一个数据库时,可将一个列定义成为一个32位的整数,并映射到FCL的Int32数据类型 ...

  9. C#学习笔记三: C#2.0泛型 可控类型 匿名方法和迭代器

    前言 C#1.0的委托特性使方法作为其他方法的参数来传递,而C#2.0 中提出的泛型特性则使类型可以被参数化,从而不必再为不同的类型提供特殊版本的实现方法.另外C#2.0还提出了可空类型,匿名方法和迭 ...

随机推荐

  1. hdu1054 Strategic Game 树形DP

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1054 思路:树形DP,用二分匹配也能解决 定义dp[root][1],表示以root 为根结点的子树且 ...

  2. [刷题]算法竞赛入门经典 3-4/UVa455 3-5/UVa227 3-6/UVa232

    书上具体所有题目:http://pan.baidu.com/s/1hssH0KO 题目:算法竞赛入门经典 3-4/UVa455:Periodic Strings 代码: //UVa455 #inclu ...

  3. CountDownLacth详解

    一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待. 用给定的计数 初始化 CounDownLatch.由于调用了countDown() 方法,所以在当前计数到达零之 ...

  4. 在Caffe上运行Cifar10示例

    准备数据集 在终端上运行以下指令: cd caffe/data/cifar10 ./get_cifar10.sh cd caffe/examples/cifar10 ./create_cifar10. ...

  5. weiphp的学习

    ##2015-07-27 **token 一定要加 不然在lists页面无法显示** ##2015-07-20 1. 自定义回复里面添加 微现场 跳转url OAuth2.0网页授权演示      & ...

  6. Java 多线程详解(三)------线程的同步

    Java 多线程详解(一)------概念的引入:http://www.cnblogs.com/ysocean/p/6882988.html Java 多线程详解(二)------如何创建进程和线程: ...

  7. Linux Network Management

    Linux网络管理 (YouTube视频教程) ISO/OSI七层模型 ISO: The International Organization for Standardization 国际标准化组织 ...

  8. php如何应对秒杀抢购高并发思路

    我们常用QPS(Query Per Second,每秒处理请求数)来衡量一个web应用的吞吐率,解决每秒数万次的高并发场景,这个指标非常关键. 举个栗子:假设一个业务请求平均为100ms,同时系统内有 ...

  9. EntityFramework6.X之DataAnnotations

    DataAnnotations 在web开发中不仅在客户端需要执行验证逻辑,会对会对用户向表单中输入的数据给出一个即时反馈:且在服务器端也需验证逻辑,因为来自网络的信息都是不能信任的.在MVC中通常是 ...

  10. MSICE界面和功能分析

    一.首页 ICE实现的这种界面样式,有可能使用WCF实现的,但是MFC来模仿也是可行的. 包括配置界面,和右下角的细节. 首页的主要功能只有3个,分别为图片拼接.视频拼接和打开拼接文件. 二.输入拼接 ...