可空值类型

C#2推出可空类型来表示可以为null的值类型。这是一个呼声很高的需求,因为在常用的数据库中都是允许某些值类型可为空的。那么为什么值类型就不能为空呢?内存中用一个全0的值来表示null,但是全0的地址说明了这个内存空间是被清除了的。所以对象选择用这种方式来初始化。用byte类型来举个例子:byte类型用8位来表示一个值,也就是说byte类型可以表示的数据最多是256个(2的8次方)。这256个值中的每一个值都是有用的,我们不可能吧其中一个值永远的舍弃掉从而来表示一个null值。对于引用类型,引用类型的变量的值在内存中在特定的操作系统中是固定的,32位是4字节,64位是8字节,32位的地址能最多表示42亿个对象,就是说这个能表示的地址太多了,所以,可以将一个全0的地址单独拿出来作为null值来用,因为地址多得永远都用不完。

先来看一下C#1中还没有可空值类型的时候是如何解决这些需求的。

方法1:使用一个魔值。确切的来说,是使用一个特定值来表示一个空值,比如DateTime.MinValue,但是这个办法很牵强,没有很好的说服力,它只是一个实现可控值类型的权宜之计。

方法2:引用类型的包装,值类型的优点之一是不需要GC去回收,把一个值类型包装成一个引用类型,GC的开销问题没有很好的解决。同时,值类型同引用类型的转换会造成装箱和拆箱,虽然C#2的泛型能够解决这个问题,但是开头说的垃圾回收没有解决。

方法3:用一个全新的值类型来表示可空值类型,这个值类型中包含一个bool值来表示是否包含一个真正的值,并包含一个表示这个值的属性。方法3正是C#2中做出的最终的决定:Nullable<T>,可空值类型。

一、定义

可空类型的核心部分是System. Nullable< T>。 除此之外,静态 类 System. Nullable 提供了一些工具方法, 可以简化可空类型的使用。

先来看一下定义:

 //
// 摘要:
// Represents a value type that can be assigned null.
//
// 类型参数:
// T:
// The underlying value type of the System.Nullable`1 generic type.
public struct Nullable<T> where T : struct
{
//
// 摘要:
// Initializes a new instance of the System.Nullable`1 structure to the specified
// value.
//
// 参数:
// value:
// A value type.
public Nullable(T value); //
// 摘要:
// Gets a value indicating whether the current System.Nullable`1 object has a valid
// value of its underlying type.
//
// 返回结果:
// true if the current System.Nullable`1 object has a value; false if the current
// System.Nullable`1 object has no value.
public bool HasValue { get; }
//
// 摘要:
// Gets the value of the current System.Nullable`1 object if it has been assigned
// a valid underlying value.
//
// 返回结果:
// The value of the current System.Nullable`1 object if the System.Nullable`1.HasValue
// property is true. An exception is thrown if the System.Nullable`1.HasValue property
// is false.
//
// 异常:
// T:System.InvalidOperationException:
// The System.Nullable`1.HasValue property is false.
public T Value { get; } //
public override bool Equals(object other);
//
// 摘要:
// Retrieves the hash code of the object returned by the System.Nullable`1.Value
// property.
//
// 返回结果:
// The hash code of the object returned by the System.Nullable`1.Value property
// if the System.Nullable`1.HasValue property is true, or zero if the System.Nullable`1.HasValue
// property is false.
public override int GetHashCode();
//
// 摘要:
// Retrieves the value of the current System.Nullable`1 object, or the object's
// default value.
//
// 返回结果:
// The value of the System.Nullable`1.Value property if the System.Nullable`1.HasValue
// property is true; otherwise, the default value of the current System.Nullable`1
// object. The type of the default value is the type argument of the current System.Nullable`1
// object, and the value of the default value consists solely of binary zeroes.
public T GetValueOrDefault();
//
// 摘要:
// Retrieves the value of the current System.Nullable`1 object, or the specified
// default value.
//
// 参数:
// defaultValue:
// A value to return if the System.Nullable`1.HasValue property is false.
//
// 返回结果:
// The value of the System.Nullable`1.Value property if the System.Nullable`1.HasValue
// property is true; otherwise, the defaultValue parameter.
public T GetValueOrDefault(T defaultValue);
//
// 摘要:
// Returns the text representation of the value of the current System.Nullable`1
// object.
//
// 返回结果:
// The text representation of the value of the current System.Nullable`1 object
// if the System.Nullable`1.HasValue property is true, or an empty string ("") if
// the System.Nullable`1.HasValue property is false.
public override string ToString(); public static implicit operator T? (T value);
public static explicit operator T(T? value);
}
  • 包含一个接受一个参数的构造函数,这个构造函数传入一个非可空值进行初始化,例如:Nullable<int> a=new Nullable<int>(5);
  • 包含一个HasValue属性来指示是否包含一个真正的值。
  • 如果HasValue返回true,可以使用Value属性来返回这个值。如果没有判断HasValue直接使用Value,可能会得到一个System.InvalidOperationException的异常。
  • Nullable是不易变的,一般来说,值类型都是不易变的,也就是说已经创建就不会再改变。值类型的复制方式就是复制一个副本,这个副本会在内存形成一个新的地址。
  • GetValueOrDefault()可以返回一个值(如果有)或者返回一个null(如果没有),它有重载的方法,向内部传入一个字面量会在没有真正的值存在的情况下拿这个传入的字面量当作一个默认值。
  • 其他的方法都是重写了object里面的。

最后, 框架提供了两个转换。 首先, 是T到Nullable< T> 的隐式转换。 转换结果为一个HasValue属性为true的实例。 同样,Nullable<T> 可以显式转换为T, 其作用与Value属性相同, 在没有真正的值可供返回时将抛出一个异常。

包装( wrapping) 和 拆 包( unwrapping)--- 将T的实例转换成Nullable<T> 的实例的过程在C#语言规范中称为包装, 相反的过程则称为拆包。 在C#语言规范中, 分别是在涉及“ 接受一个参数的构造函数” 和 Value 属性时定义的这两个术语。

二、Nullable<T>的装箱和拆箱

它是值类型,所以会涉及到装箱和拆箱。

装箱:Nullable<T>的实例要么装箱成空引用(如果没有值),要么装箱成T的一个已装箱值。不存在“装箱的可空int”。

 Nullable<int> nullable = 5;
object boxed = nullable;
Console.WriteLine(boxed.GetType());//这里会输出System.Int32,印证了不存在“装箱的可空int”。

拆箱:已装箱的值要么拆箱成一个普通类型,要不拆箱成对应的可空类型。拆箱一个空引用时,如果拆箱成普通类型,会抛出System.NullReferenceException:

 Nullable<int> a = new Nullable<int>();
object b = a;
int c = (int) b;//System.NullReferenceException

但如果拆箱成恰当的可空值类型,则会产生一个没有值的实例。下面的则不会抛出错误:

 Nullable<int> a = new Nullable<int>();
object b = a;
var c = (Nullable<int>) b;

三、Nullable<T>实例的相等性

Nullable< T> 覆盖了object.Equals( object),注意这个方法需要传入一个object的类型的实例,所以,传入的Nullable会进行装箱。Nullable装箱的原理上面有描述。这个方法基本与我们期望的一致,没有什么大的问题。

四、来自非泛型Nullable类的支持

Nullable有两个方法:

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

Compare是用Compare<T>.Default来比较两个的大小:

 Nullable<int> a = ;
Nullable<int> b = ;
var defaults = Comparer<int?>.Default;
Console.WriteLine(defaults.Compare(a,b));//0

Equals使用EqualityComparer< T>. Default。

对于没有值的实例, 上述每个方法返回的值都遵从.NET的约定:空值与空值相等, 小于其他所有值。

五、语法糖

?。使用这个修饰符放到int后面,int?的含义与Nullable<int>的含义完全一样,包括生成的IL。就像int和System.Int32换着用一样。

与null值的比较

可以将null赋给一个可空值类型的实例,表示一个没有真实值的可空值类型。

int? a = null;

可空值类型和其基础类型的一些匹配的行为

标题中提到的基础类型是这么一个概念:int?的基础类型就是int。

假如一个非可空的值类型支持一个操作符或者一种转换,而且那个操作符或者转换只涉及其他非可空的值类型时, 那么可空的值类型也支持相同的操作符或转换。 下面举一个更具体的例子。 我们知道,int到long存在着一个隐式转换, 这就意味着 int? 到 long? 也存在一个隐式转换, 其行为可想而知。

因为int和long存在这么一种隐式转换的规则,那么,就存在:

int?到long?的隐式转换

int到long?的隐式转换

int?到long的显示转换。

int a = ;
long? b = a;//隐式转换
int? c = ;
long? d = c;//隐式转换
int? e = ;
long f =(long) e;//显示的转换

转换和操作符

将基础类型的一些转换行为应用到对应的可空类型上的转换行为叫做提升转换,将基础类型的一些操作符的行为应用到相应可空值类型的操作符叫做提升操作符。基础类型重载操作符后,对应的可空类型就可享用这个重载。

这里的只是还需要从书中进行补充,未完待续。。。。

空合并操作符

这个二元操作符在对 first ?? second 求值 时, 大致会经历以下步骤:

1、对first进行求值

2、如果结果非空,表达式的值和类型就已经确定

3、如果结果为空,继续计算second的值并将结果的值作为整个表达式的值。

 int? a = null;
int c = a ?? ;

很明显这里涉及到了类型转换的问题,我们可以直接将int类型的c变量赋给这个表达式,因为second是非可空的。

C#复习笔记(3)--C#2:解决C#1的问题(可空值类型)的更多相关文章

  1. Java基础复习笔记系列 八 多线程编程

    Java基础复习笔记系列之 多线程编程 参考地址: http://blog.csdn.net/xuweilinjijis/article/details/8878649 今天的故事,让我们从上面这个图 ...

  2. Angular复习笔记7-路由(下)

    Angular复习笔记7-路由(下) 这是angular路由的第二篇,也是最后一篇.继续上一章的内容 路由跳转 Web应用中的页面跳转,指的是应用响应某个事件,从一个页面跳转到另一个页面的行为.对于使 ...

  3. Angular复习笔记7-路由(上)

    Angular复习笔记7-路由(上) 关于Angular路由的部分将分为上下两篇来介绍.这是第一篇. 概述 路由所要解决的核心问题是通过建立URL和页面的对应关系,使得不同的页面可以用不同的URL来表 ...

  4. Angular复习笔记6-依赖注入

    Angular复习笔记6-依赖注入 依赖注入(DependencyInjection)是Angular实现重要功能的一种设计模式.一个大型应用的开发通常会涉及很多组件和服务,这些组件和服务之间有着错综 ...

  5. tarjan复习笔记

    tarjan复习笔记 (关于tarjan读法,优雅一点读塔洋,接地气一点读塔尖) 0. 连通分量 有向图: 强连通分量(SCC)是个啥 就是一张图里面两个点能互相达到,那么这两个点在同一个强连通分量里 ...

  6. 树的直径,LCA复习笔记

    前言 复习笔记第6篇. 求直径的两种方法 树形DP: dfs(y); ans=max( ans,d[x]+d[y]+w[i] ); d[x]=max( d[x],d[y]+w[i] ); int di ...

  7. 状压DP复习笔记

    前言 复习笔记第4篇.CSP RP++. 引用部分为总结性内容. 0--P1433 吃奶酪 题目链接 luogu 题意 房间里放着 \(n\) 块奶酪,要把它们都吃掉,问至少要跑多少距离?一开始在 \ ...

  8. 斜率优化DP复习笔记

    前言 复习笔记2nd. Warning:鉴于摆渡车是普及组题目,本文的难度定位在普及+至省选-. 参照洛谷的题目难度评分(不过感觉部分有虚高,提高组建议全部掌握,普及组可以选择性阅读.) 引用部分(如 ...

  9. Java基础复习笔记系列 九 网络编程

    Java基础复习笔记系列之 网络编程 学习资料参考: 1.http://www.icoolxue.com/ 2. 1.网络编程的基础概念. TCP/IP协议:Socket编程:IP地址. 中国和美国之 ...

随机推荐

  1. 【项目 · Wonderland】需求规格说明书 · 终版

    [项目 · Wonderland]需求规格说明书 · 终版 Part 0 · 简 要 目 录 Part 1 · 流 程 / 分 工 Part 2 · 需 求 规 格 说 明 书 Part 1 · 流 ...

  2. Nginx 的 TCP 负载均衡介绍

    Nginx除了以前常用的HTTP负载均衡外,Nginx增加基于TCP协议实现的负载均衡方法. HTTP负载均衡,也就是我们通常所有“七层负载均衡”,工作在第七层“应用层”.而TCP负载均衡,就是我们通 ...

  3. 早期nginx tcp代理(基于patch实现)

    nginx tcp代理功能由nginx_tcp_proxy_module模块提供,同时监测后端主机状态.该模块包括的模块有: ngx_tcp_module, ngx_tcp_core_module, ...

  4. 使用IntelliJ IDEA和Maven管理搭建Web开发环境(以Spring MVC为例)(二)

    前言:在使用IntelliJ IDEA和Maven管理搭建Web开发环境(以Spring MVC为例)(一)中已经介绍了如何对web基础环境进行搭建,这里主要演示,如何对spring环境进行搭建,然后 ...

  5. PHP 缓存技术(一)

    移除光盘

  6. 从 0 → 1,学习Linux该这么开始!

    首先我们还是来普及以下概念,讲点虚的.现在是图形系统的天下,windows我们用了20多年.成功归功与它图形界面,你会点鼠标吗你会敲键盘吗?所以你会上网会聊天会玩游戏了.那么,0基础接触的Linux, ...

  7. USB知识汇总

    概述 通用串行总线(英语:Universal Serial Bus,缩写:USB)是连接计算机系统与外部设备的一种串口总线标准,也是一种输入输出接口的技术规范,被广泛地应用于个人电脑和移动设备等信息通 ...

  8. POJ2749 Building roads

    嘟嘟嘟 最近把21天漏的给不上. 今天重温了一下2-SAT,感觉很简单.就是把所有条件都转化成如果--必然能导出--.然后就这样连边建图,这样一个强连通分量中的所有点必然都是真或者假.从而根据这个点拆 ...

  9. docker 14 dockerfile自定义mycentos

    Base镜像(scratch) Docker Hub中99%的镜像是通过在base镜像中安装和配置需要的软件构建出来的.也就是说base镜像是所有其他镜像的鼻祖. hub默认centos镜像是什么情况 ...

  10. 阿里云配置tomcat https

    阿里云申请免费的SSL证书和配置https,可参考该篇博文:https://blog.csdn.net/baidu_19473529/article/details/70037976 虽然有现成的,不 ...