在程序开发中,有时候需要值类型也为可空类型,比如,在数据库中,我们可以把一个日期Datetime设置为null。

在C# 2.0中就出现了可空类型,允许值类型也可以为空(null),可空类型的实现基于C#泛型。

可空类型基本知识


可空类型的核心是System.Nullable<T>,同时静态类System.Nullable为可空类型提供了很多实用的方法。下面分别看看可空类型的这两个重要组成部分。

System.Nullable<T>


通过ILSpy我们可以查看这个类型的C#代码:

从上面的图中可以看到关于System.Nullable<T>的一些关键点:

  1. Nullable<T>是一个泛型类型
  2. 类型参数T有一个值类型的约束(根据值类型约束T : struct,T不能为可空类型,也就是说Nullable<Nullable<int>>是不允许的)
  3. Nullable<T>是一个值类型(是一个struct)

对于任何具体的可空类型来说,T的类型为可空类型的基础类型(underlying type),例如Nullable<int>的基础类型就是int。

通过上面代码还可以看到,Nullable<T>有两个重要的属性,HasValue和Value。通过它们可以了解可空类型是怎么工作的:

  1. 如果一个可空值类型存在一个真正的值,那么Value就代表这个值本身,同时HasValue值为true
  2. 如果一个可空值类型为空,那么HasValue为false,Value这是没有意义。

下面看一个可空类型的简单例子,进一步了解一下可空类型:

static void Display(Nullable<int> x)
{
Console.WriteLine("HasValue: {0}", x.HasValue);
if (x.HasValue)
{
Console.WriteLine("Value: {0}", x.Value);
Console.WriteLine("Explicit conversion: {0}", (int)x);
} Console.WriteLine("GetValueOrDefault(): {0}", x.GetValueOrDefault());
Console.WriteLine("GetValueOrDefault(10): {0}", x.GetValueOrDefault()); Console.WriteLine("ToString(): {0}", x.ToString());
Console.WriteLine("GetHashCode(): {0}", x.GetHashCode());
Console.WriteLine(); } static void Main(string[] args)
{
Nullable<int> x = ;
Display(x);
x = new Nullable<int>();
Display(x); x = new Nullable<int>();
Display(x); Console.Read();
}

程序的输出为:

通过这段代码可以看到HasValue和Value的使用,以及Nullable<T>中一些常用的方法。

注意,在这段代码中,下面两句的IL代码是一样的:

C#代码

Nullable<int> x = ;
x = new Nullable<int>();

IL代码

IL_0004: call instance void valuetype [mscorlib]System.Nullable`<int32>::.ctor(!)
IL_0015: call instance void valuetype [mscorlib]System.Nullable`<int32>::.ctor(!)

这里涉及了包装(wrapping)拆包(unwrapping)的概念:将T的一个实例转换成Nullable<T>的一个实例的过程在C#中成为包装,相反的过程成为拆包。这个概念跟装箱和拆箱不一样,后面会看到Nullable<T>的装箱和拆箱。

Nullable<T>的装箱和拆箱


从前面的分析可以看到Nullable<T>是一个结构,也就是一个值类型。也就是说,当我们把可空类型转换成一个引用类型的时候需要进行装箱操作。

对于Nullable<T>的装箱和拆箱可以概括为:

  • Nullable<T>的实例要么装箱为空引用,要么装箱成T的一个以装箱的值

  • 已装箱的值可以拆箱成普通类型,或者拆箱为对于的可空类型

    • 拆箱一个空引用时,如果拆箱为普通类型,会抛出一个NullReferenceException的异常
    • 如果拆箱成恰当的可空类型,就会拆箱成一个没有值的Nullable<T>实例

看一个关于可空类型装箱和拆箱的例子:

static void Main(string[] args)
{
Nullable<int> x = ;
//有值的可空类型装箱
object boxed = x;
Console.WriteLine(x.GetType()); //拆箱为普通类型
int normal = (int)boxed;
Console.WriteLine(normal); //拆箱为可空类型
x = (Nullable<int>)boxed;
Console.WriteLine(x); x = new Nullable<int>(); //空的可空类型装箱
boxed = x;
Console.WriteLine(boxed == null); //拆箱为可空类型
x = (Nullable<int>)boxed;
Console.WriteLine(x.HasValue);
}

输出:

System.Nullable


System.Nullable是一个静态类,只包含三个静态方法,大家可以通过ILSpy进行查看,这里就不上图了。

下面两个方法是比较方法:

public static int Compare<T>(T? n1, T? n2) where T : struct
public static bool Equals<T>(T? n1, T? n2) where T : struct

下面这个方法用来获得可空类型的基础类型:

public static Type GetUnderlyingType(Type nullableType)

可空类型语法糖


在C# 2.0中,我们可以使用?修饰符来表示可空类型。

下面的C#语句具有相同的IL代码。

Nullable<int> x = ;
int? y = ;
IL_0004: call instance void valuetype [mscorlib]System.Nullable`<int32>::.ctor(!)

IL_000d: call instance void valuetype [mscorlib]System.Nullable`<int32>::.ctor(!)

使用null进行赋值和比较


C#编译器允许使用null在比较和赋值中表示一个可空类型的空值。

对于下面的代码,通过IL可以发现"x == null"实际调用的是HasValue属性进行比较。

int? x = null;
Console.WriteLine(x == null);
IL_000b: call instance bool valuetype [mscorlib]System.Nullable`<int32>::get_HasValue()

总结


C# 2.0中出现的可空类型解决了我们很多的问题,可空类型的相关知识还是比较容易理解的。

在使用中,我们可以直接使用?修饰符来创建可空值类型。

原文链接

C#可空类型(转载)的更多相关文章

  1. 雷林鹏分享:C# 可空类型(Nullable)

    C# 可空类型(Nullable) C# 可空类型(Nullable) C# 提供了一个特殊的数据类型,nullable 类型(可空类型),可空类型可以表示其基础值类型正常范围内的值,再加上一个 nu ...

  2. 四、可空类型Nullable<T>到底是什么鬼

    值类型为什么不可以为空 首先我们都知道引用类型默认值都是null,而值类型的默认值都有非null. 为什么引用类型可以为空?因为引用类型变量都是保存一个对象的地址引用(就像一个url对应一个页面),而 ...

  3. HTML之DocType的几种类型 -转载

    HTML之DocType的几种类型转载 doctype类型详细doctype的几种类型html之doctype 分类: 前端文摘  在默认情况下,FF和IE的解释标准是不一样的,也就是说,如果一个网页 ...

  4. C#可空类型

    C#创建可空类型对于有些可选类型的时候特别好用.创建可空类型用法直接上图. 执行效果 用法 运行效果

  5. 【C#】可空类型(Nullable)

    C# 可空类型(Nullable) C# 提供了一个特殊的数据类型,nullable 类型(可空类型),可空类型可以表示其基础值类型正常范围内的值,再加上一个 null 值. 例如,Nullable& ...

  6. C#可空类型的速度和GC Alloc测试

    在Unity中进行速度和GC Alloc的测试 测试脚本: using UnityEngine; using System; using System.Collections; using Syste ...

  7. swift_枚举 | 可为空类型 | 枚举关联值 | 枚举递归 | 树的概念

    ***************可为空的类型 var demo2 :we_demo = nil 上面这个代码串的语法是错的 为什么呢, 在Swift中,所有的类型定义出来的属性的默认值都不可以是nil ...

  8. Guava-Optional可空类型

    接上篇Guava之Joiner和Splitter,本篇将介绍Guava的另外一个有用的对象Optional,这在Java中Google Guava首先给我们提出可空对象模型的.在其他语言如c#这是已经 ...

  9. [C#] 可空类型的实现原理

    int? 是可为null的值类型.只比int多一个值就是null. 思考: 同样的内存空间,怎么实现的多一个值的?都是4字节,32位,int?靠什么存在一个null值的. 发现: 分析一下内存,看看如 ...

随机推荐

  1. 解决input中智能提示框onblur与onclick冲突的问题

    背景: 制作一个类似百度输入法的智能提示框, 其中当关键词输入进来时,会有智能提示展开,实际需求是当点击智能提示框的汉字时,输入框中自动补全并关闭智能提示, 当点击其他区域时,智能提示框自动隐藏,如下 ...

  2. [HNOI2011]括号修复

    设\(nd[4]\) 0--多出来的右括号 1--多出来的左括号 2--取反后多出来的右括号 3--取反后多出来的左括号 这样一来 Swap: swap(0,3),swap(1,2),swap(sn[ ...

  3. 高性能JavaScript(高性能Ajax)

    ajax是一种与服务器通信而无需重载页面的方法(即局部刷新.) 高性能的Ajax应该考虑数据传输技术和数据格式,以及其他的如数据缓存等优化技术. 请求数据 请求数据的常用技术有XMLHttpReque ...

  4. JS--我发现,原来你是这样的JS(四)(看看变量,作用域,垃圾回收机制是啥)

    一.介绍 这是红宝书(JavaScript高级程序设计 3版)的读书笔记第四篇,是红宝书第四章内容(主要是变量和作用域问题),当然其中还有我个人的理解.红宝书这本书可以说是难啃的,要看完不容易,挺厚的 ...

  5. Python学习笔记之——requests库

    requests库一个优雅而简单的用于Python的HTTP库,可以极大的简化我们发送http请求及获取响应的代码. requests是python的第三方库,所以使用之前需要先安装. 1.安装之后就 ...

  6. MAC安装了mumu安卓模拟器,但无法检测到该模拟器

    1.adb   devices  看不到模拟器 2.adb connect 127.0.0.1:5555 3.adb kill-server 没有报错,即成功 4. adb start-server ...

  7. Node路由简单的处理

    看过node很多例子,都是将路由直接放到入口文件中处理,使得文件显得很大很乱,特别是当一个项目变大,有上百甚至上千的路由,那该怎么办? 最近在想如何将一个个的路由放到一个单独的模块中处理,比如'/us ...

  8. LeetCode题解之Merge k Sorted Lists 解法二

    1.题目描述 2.分析 利用 vector 存储指针,同时合并k个链表. 3.代码 ListNode* mergeKLists(vector<ListNode*>& lists) ...

  9. Oracle EBS SLA取值

    -- 从GL总账追溯到 => 子分类账SLA => 子模块AP.AR等 SELECT xep.name, -- 法人主体 xep.legal_entity_identifier, -- 法 ...

  10. Oracle EBS AR 冲销收款

    DECLARE L_CR_ID NUMBER; L_ATTRIBUTE_REC AR_RECEIPT_API_PUB.ATTRIBUTE_REC_TYPE; L_GLOBAL_ATT_REC AR_R ...