C#基础知识 全面解析可空类型

引言:

  C# 2.0 中还引入了可空类型,可空类型也是值类型,只是可空类型是包括null的值类型的,下面就介绍下C#2.0中对可空类型的支持具体有哪些内容(最近一直都在思考如何来分享这篇文章的,因为刚开始觉得可空类型使用过程中比较简单,觉得没有讲的必要,但是考虑到这个系列的完整性,决定还是唠叨下吧,希望对一些不熟悉的人有帮助)。

一、为什么会有可空类型

  如果朋友们看了我之前的分享,对于这一部分都不会陌生,因为我一般介绍C#特性经常会以这样的方式开头的, 因为每个特性都是有它出现的原因的(有一句佛语这是这么讲的:万事皆有因,有因必有果),首先来说说这个因的(果当然是新增加了可空类型这个新特性了。),当我们在设计数据库的时候,我们可以设置数据库字段允许为null值,如果数据库字段是日期等这样在C#语言是值类型时,当我们把数据库表映射一个对象时,此时Datetime类型在C# 语言中是不能为null的,如果这样就会与数据库的设计有所冲突,这样开发人员就会有这样的需求了——值类型能不能也为可空类型的?同时微软也看出了用户有这样的需求,所以微软在C# 2.0中就新增加了一种类型——可空类型,即包含null值的值类型,这个也就是我理解的因了,介绍完因之后,当然就是好好唠叨下可空类型是个什么东西的了?

二、可空类型的介绍

  可空类型也是值类型,只是它是包含null的一个值类型。我们可以像下面这样表示可空类型(相信大家都不陌生):

代码如下:

int? nullable = null;

  上面代码 int? 就是可空的int类型(有人可能会这样的疑问的, 如果在C#1中我硬要让一个值类型为一个可空类型怎么办到呢?当然这个在C#1之前也是有可以办到的,只是会相当麻烦,对于这个如果有兴趣的朋友可以去刨下根),然而其实 "?"这个修饰符只是C#提供的一个语法糖(所谓语法糖,就是C#提供的一种方便的形式,其实肯定没有int? 这个类型,这个int?编译器认为的就是Nullable<int>类型,即可空类型),其实真真C# 2.0提供的可空类型是——Nullable<T>(这个T就是上专题介绍的泛型参数,其中T只能为值类型,因为从可空类型的定义为:public struct Nullable<T> where T : struct)和Nullable。下面给出一段代码来介绍可空类型的使用:


代码如下:

namespace 可空类型Demo
{
class Program
{
static void Main(string[] args)
{
// 下面代码也可以这样子定义int? value=1;
Nullable<int> value = 1;

Console.WriteLine("可空类型有值的输出情况:");
Display(value);
Console.WriteLine();
Console.WriteLine();

value = new Nullable<int>();
Console.WriteLine("可空类型没有值的输出情况:");
Display(value);
Console.Read();
}

// 输出方法,演示可空类型中的方法和属性的使用
private static void Display(int? nullable)
{
// HasValue 属性代表指示可空对象是否有值
// 在使用Value属性时必须先判断可空类型是否有值,
// 如果可空类型对象的HasValue返回false时,将会引发InvalidOperationException异常
Console.WriteLine("可空类型是否有值:{0}", nullable.HasValue);
if (nullable.HasValue)
{
Console.WriteLine("值为: {0}", nullable.Value);
}

// GetValueOrDefault(代表如果可空对象有值,就用它的值返回,如果可空对象不包含值时,使用默认值0返回)相当与下面的语句
// if (!nullable.HasValue)
// {
// result = d.Value;
// }

Console.WriteLine("GetValueorDefault():{0}", nullable.GetValueOrDefault());

// GetValueOrDefault(T)方法代表如果 HasValue 属性为 true,则为 Value 属性的值;否则为 defaultValue 参数值,即2。
Console.WriteLine("GetValueorDefalut重载方法使用:{0}", nullable.GetValueOrDefault(2));

// GetHashCode()代表如果 HasValue 属性为 true,则为 Value 属性返回的对象的哈希代码;如果 HasValue 属性为 false,则为零
Console.WriteLine("GetHashCode()方法的使用:{0}", nullable.GetHashCode());
}
}
}

输出结果:

上面的演示代码中都注释,这里就不再解释了,为了让大家明白进一步理解可空类型是值类型,下面贴出中间语言代码截图:

三、空合并操作符(?? 操作符)

  ??操作符也就是"空合并操作符",它代表的意思是两个操作数,如果左边的数不为null时,就返回左边的数,如果左边的数为null,就返回右边的数,这个操作符可以用于可空类型,也可以用于引用类型,但是不能用于值类型(之所以不能应用值类型(这里除了可空类型),因为??运算符要对左边的数与null进行比较,然而值类型,不能与null类型比较,所以就不支持??运算符),下面用一个例子来掩饰下??运算符的使用(??这个运算符可以方便我们设置默认值,可以避免在代码中写if, else语句,简单代码数量,从而有利于阅读。)

代码如下:

static void Main(string[] args)
{
Console.WriteLine("??运算符的使用如下:");
NullcoalescingOperator();
Console.Read();
}

private static void NullcoalescingOperator()
{
int? nullable = null;
int? nullhasvalue = 1;

// ??和三目运算符的功能差不多的
// 所以下面代码等价于:
// x=nullable.HasValue?b.Value:12;
int x = nullable ?? 12;

// 此时nullhasvalue不能null,所以y的值为nullhasvalue.Value,即输出1
int y = nullhasvalue ?? 123;
Console.WriteLine("可空类型没有值的情况:{0}",x);
Console.WriteLine("可空类型有值的情况:{0}", y);

// 同时??运算符也可以用于引用类型, 下面是引用类型的例子
Console.WriteLine();
string stringnotnull = "123";
string stringisnull = null;

// 下面的代码等价于:
// (stringnotnull ==null)? "456" :stringnotnull
// 同时下面代码也等价于:
// if(stringnotnull==null)
// {
// return "456";
// }
// else
// {
// return stringnotnull;
// }
// 从上面的等价代码可以看出,有了??运算符之后可以省略大量的if—else语句,这样代码少了, 自然可读性就高了
string result = stringnotnull ?? "456";
string result2 = stringisnull ?? "12";
Console.WriteLine("引用类型不为null的情况:{0}", result);
Console.WriteLine("引用类型为null的情况:{0}", result2);
}

下面是运行结果截图:

四、可空类型的装箱和拆箱

  值类型存在装箱和拆箱的过程,可空类型也属于值类型,从而也有装箱和拆箱的过程的, 这里先介绍下装箱和拆箱的概念的, 装箱指的的从值类型到引用类型的过程,拆箱当然也就是装箱的反过程,即从引用类型到值类型的过程(这里进一步解释下我理解的装箱和拆箱,首先.Net中值类型是分配在堆栈上的,然而引用类型分配在托管堆上,装箱过程就是把值类型的值从推栈上拷贝到托管堆上,然后推栈上存储的是对托管堆上拷贝值的引用,然而拆箱就是把托管堆上的值拷贝到堆栈上.简单一句话概况,装箱和拆箱就是一个值的拷贝的一个过程,就想搬家一样,把东西从一个地方搬到另一个地方,对于深入的理解,大家可以参考下园中的博文.), 括号中是我理解的装箱和拆箱的过程,下面就具体介绍下可空类型的装箱和拆箱的:

  当把一个可空类型赋给一个引用类型变量时,此时CLR 会对可空类型(Nullable<T>)对象进行装箱处理,首先CLR会检测可空类型是否为null,如果为null,CLR则不进行实际的装箱操作(因为null可以直接赋给一个引用类型变量),如果不为null,CLR会从可空类型对象中获取值,并对该值进行装箱(这个过程就是值类型的装箱过程了。),当把一个已装箱的值类型赋给一个可空类型变量时,此时CLR会对已装箱的值类型进行拆箱处理,如果已装箱值类型的引用为null,此时CLR会把可空类型设为null(如果觉得啰嗦,大家可以直接看下面的代码,代码中也会有详细的注释)。下面用一个示例来演示下可空类型的装箱和拆箱的使用,这样可以帮助大家更好的理解前面介绍的概念:

代码如下:

static void Main(string[] args)
{
//Console.WriteLine("??运算符的使用如下:");
//NullcoalescingOperator();
Console.WriteLine("可空类型的装箱和拆箱的使用如下:");
BoxedandUnboxed();
Console.Read();
}

// 可空类型装箱和拆箱的演示
private static void BoxedandUnboxed()
{
// 定义一个可空类型对象nullable
Nullable<int> nullable = 5;
int? nullablewithoutvalue = null;

// 获得可空对象的类型,此时返回的是System.Int32,而不是System.Nullable<System.Int32>,这点大家要特别注意下的
Console.WriteLine("获取不为null的可空类型的类型为:{0}",nullable.GetType());

// 对于一个为null的类型调用方法时出现异常,所以一般对于引用类型的调用方法前,最好养成习惯先检测下它是否为null
//Console.WriteLine("获取为null的可空类型的类型为:{0}", nullablewithoutvalue.GetType());

// 将可空类型对象赋给引用类型obj,此时会发生装箱操作,大家可以通过IL中的boxed 来证明
object obj = nullable;

// 获得装箱后引用类型的类型,此时输出的仍然是System.Int32,而不是System.Nullable<System.Int32>
Console.WriteLine("获得装箱后obj 的类型:{0}", obj.GetType());

// 拆箱成非可空变量
int value = (int)obj;
Console.WriteLine("拆箱成非可空变量的情况为:{0}", value);

// 拆箱成可空变量
nullable = (int?)obj;
Console.WriteLine("拆箱成可空变量的情况为:{0}", nullable);

// 装箱一个没有值的可空类型的对象
obj = nullablewithoutvalue;
Console.WriteLine("对null的可空类型装箱后obj 是否为null:{0}", obj==null);

// 拆箱成非可空变量,此时会抛出NullReferenceException异常,因为没有值的可空类型装箱后obj等于null,引用一个空地址
// 相当于拆箱后把null值赋给一个int 类型的变量,此时当然就会出现错误了
//value = (int)obj;
//Console.WriteLine("一个没有值的可空类型装箱后,拆箱成非可空变量的情况为:{0}", value);

// 拆箱成可空变量
nullable = (int?)obj;
Console.WriteLine("一个没有值的可空类型装箱后,拆箱成可空变量是否为null:{0}", nullable == null);
}

运行结果:

  上面代码中都有注释的, 而且代码也比较简单, 这里就不解释了, 其实可空类型的装箱和拆箱操作大家可以就理解为非可空值类型的装箱和拆箱的过程,只是对于非可空类型因为包含null值,所以CLR会提前对它进行检查下它是否为空,为null就不不任何处理,如果不为null,就按照非可空值类型的装箱和拆箱的过程来装箱和拆箱。

五、小结

  到这里本专题的介绍就完成了,本专题主要介绍了下可空类型以及可空类型相关的知识,希望这篇文章可以帮助大家对可空类型的认识可以更加全面,下一个专题将和大家介绍下匿名方法, 匿名方法也是Lambda表达式和Linq的一个铺垫,然而它是C#2中被提出来了的, 从而可以看出Lambda和Linq在C# 3.0中被添加其实是微软早在C# 2.0的时候就计划好了的,早就计划好了的(这也是我的推断,然而我觉得为什么它不直接在把Lambda和Linq都放在C# 2中提出来的, 却偏偏放在C# 3.0中提出,我理解原因有——1 觉得微软当时肯定是想一起提出的,但是后面发现这几个新的特性提出后会对编译器做比较大的改动,需要比较长的时间来实现,此时又怕用户等不及了,觉得C#很多东西都没有,所以微软就先把做好了的部分先发布出来,然而把Lambda和Linq放到C#3来提出。我推理觉得应该是这样的,所以C#的所有特性都是紧密相连的。)

(0)

相关推荐

  • C#难点逐个击破(8):可空类型System.Nullable

    null与void null值用来表示数据类型未被赋予任何值,它是一种引用类型:void表示没有类型,或者说是没有任何值.null与void的区别可以认为void是根本没有,而null是一个空箱子,里面什么都没有. null值只能赋值给引用类型,这里注意到string也是一种引用类型:引用类型在C中称为"指针",即存放变量位置的内存空间位置.将变量设为null,会显式地设置引用,且它本身不指向任何内存位置: null值赋给值类型将导致编译错误. void用于方法值的返回,其本质并不是一

  • C#可空类型用法分析

    本文实例讲述了C#可空类型用法.分享给大家供大家参考.具体分析如下: 可空类型只能用于值类型,不能用于引用类型,引用类型本身支持null值. 如果直接把null赋值给int类型,会出现一个错误"无法将 NULL 转换成'int',因为它是一种值类型". 赋值时应该使用 int? a = null; 可空类型与一元或二元运算符一起使用是,如果一个操作数或两个操作数都是null,其结果为null. 说白了int?a=null 这句话的意思就是让值类型a可以接收null这个值 举例1 int

  • C# 可空类型分析

    例如,Nullable<Int32>,读作"可空的 Int32",可以被赋值为 -2147483648 到 2147483647 之间的任意值,也可以被赋值为 null 值.Nullable<bool> 可以被赋值为 true 或 false,或 null.在处理数据库和其他包含可能未赋值的元素的数据类型时,将 null 赋值给数值类型或布尔型的功能特别有用.例如,数据库中的布尔型字段可以存储值 true 或 false,或者,该字段也可以未定义. 可空类型具有

  • C#中关于可空类型的小知识

    1) 在与1元或二元运算符一起使用时,如果其中一个操作数为null或两个均null,则结果为null. 如: 复制代码 代码如下: int? a = null; int? b = a+3; 则b为null 2) 比较可空类型时,只要有一个操作数为null,结果就为false 如: 复制代码 代码如下: int? a = null; int? b = 8; (a>=b)将返回false 而正是因为null的可能性,所以不能随意比较一个可空类型和一个非可空类型. 3) 空接合运算符 空接合运算符&quo

  • c#可空类型的作用说明

    可空类型用途主要是从数据库读取数据有可能为空,而不是插入使用,插入数据都要进行验证,如果要插入数据库的null,则使用DBNull.value 判断取出的数据是否为数据库中的null,如果是则赋值给可空的datetime类型的Lockdatatime 属性 tseat.Lockdatatime = reader.IsDBNull(5) ? null : (DateTime?)reader.GetDateTime(5); 判断取出的数据是否为数据库中的null,如果是则赋值给可空的int类型的Te

  • C#基础知识 全面解析可空类型

    引言: C# 2.0 中还引入了可空类型,可空类型也是值类型,只是可空类型是包括null的值类型的,下面就介绍下C#2.0中对可空类型的支持具体有哪些内容(最近一直都在思考如何来分享这篇文章的,因为刚开始觉得可空类型使用过程中比较简单,觉得没有讲的必要,但是考虑到这个系列的完整性,决定还是唠叨下吧,希望对一些不熟悉的人有帮助). 一.为什么会有可空类型 如果朋友们看了我之前的分享,对于这一部分都不会陌生,因为我一般介绍C#特性经常会以这样的方式开头的, 因为每个特性都是有它出现的原因的(有一句佛

  • C++基础知识实例解析(一)

    今天小编和大家一起通过几个实例学习C++基础知识,下面进行实例解析: [1-1]编写一个程序,实现一个整数.长整数.浮点数和双精度数除以2的计算. [分析]这是一个典型的函数重载的程序.声明函数div()为除法函数,每个函数的功能基本都是一致的,不同的只是形式参数的类型不同而已.程序代码如下: #include <iostream> using namespace std; int division(int x){ return x/2; } long division(long x){ re

  • Java 网络爬虫基础知识入门解析

    前言 说起网络爬虫,大家想起的估计都是 Python ,诚然爬虫已经是 Python 的代名词之一,相比 Java 来说就要逊色不少.有不少人都不知道 Java 可以做网络爬虫,其实 Java 也能做网络爬虫而且还能做的非常好,在开源社区中有不少优秀的 Java 网络爬虫框架,例如 webmagic .我的第一份正式工作就是使用 webmagic 编写数据采集程序,当时参与了一个舆情分析系统的开发,这里面涉及到了大量网站的新闻采集,我们就使用了 webmagic 进行采集程序的编写,由于当时不知

  • Bootstrap页面布局基础知识全面解析

    Bootstrap作为支持响应式布局的一个前端插件,确实发挥着重要的作用,无论你是在手机,平板还是PC上浏览网页,都能达到不错的效果,这一切一切,都是bootstrap带给我们的! 今天主要说下页面的布局,这是最基础的东西了,当我们设计一个站点时,应该为它设计一个全局性的统一的规范页面,这种页面我们叫它布局页,而在页面上体现出来的东西,就是布局的元素,在bootstrap里当然也是不可缺少的东西. Bootstrap的布局是一种栅格系统,即它由行和列组成,在使用时需要为页面内容和栅格系统包裹一个

  • Python Matplotlib绘图基础知识代码解析

    1.Figure和Subplot import numpy as np import matplotlib.pyplot as plt #创建一个Figure fig = plt.figure() #不能通过空figure绘图,必须使用add_subplot创建一个或多个subplot #图像为2x2,第三个参数为当前选中的第几个 ax1 = fig.add_subplot(2, 2, 1) ax2 = fig.add_subplot(2, 2, 2) ax3 = fig.add_subplot

  • C# 可空类型的具体使用

    在项目中我们经常会遇到可为空类型,那么到底什么是可为空类型呢?下面我们将从4个方面为大家剖析. 1.可空类型基础知识 顾名思义,可空类型指的就是某个对象类型可以为空,同时也是System.Nullable结构的实例.可空类型可以表示基础值类型正常范围的值,然后再加上一次null值.当然,也可以被赋值null值.比如Nullable<bool>可以被赋值true.false或者null. 声明一个可空类型的标准语句如下: T? myNullableNum = rangedNum/new T?()

  • Lua教程(二):基础知识、类型与值介绍

    一.基础知识: 1. 第一个程序和函数:     在目前这个学习阶段,运行Lua程序最好的方式就是通过Lua自带的解释器程序,如:   复制代码 代码如下: /> lua     > print("Hello World")     Hello World 这样我们就可以以交互性的方式输入lua代码,并立即得到执行结果了.对于代码块较少的测试程序来说,这种方式确实是非常方便的,然而对于相对复杂的程序而言,这种方式就不是很合适了.如果是这样,我们可以将Lua代码保存到一个独立

  • Java异常基础知识解析

    Java程序运行的非正常现象叫做运行错误,根据其性质可分为两类:错误(Error)和异常(Exception); 他们有一个共同的父类(也是所有异常的顶级父类):Throwable. 异常类结构 Error Error(错误)由JVM生成并抛弃不做处理:此类错误通常与代码和执行的操作无关,是虚拟机中出现了比较严重的问题,程序本身无法解决(常见的错误有死循环.内存泄漏等). 一个常见的错误为Java虚拟机错误(VirtualMachineError),当JVM不再有继续执行操作所需的内存资源时,将

  • C#基础语法:可空类型详解

    以下是System.Nullable<T>在FCL中的定义. [Serializable, StructLayout(LayoutKind.Sequential)] public struct Nullable<T> where T :struct { private Boolean hasValue= false; internal T value= default(T); public Nullable(T value) { this.value= value; this.ha

  • java基础知识 super和this使用解析

    这篇文章主要介绍了java基础知识 super和this使用解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 super关键字的用法有三种: 1.在子类的成员方法中,访问父类的成员变量 2.在子类的成员方法中,访问父类的成员方法 3.在子类的构造方法中,访问父类的构造方法 this和super区别 super关键字用来访问父类内容,this关键字用来访问本类内容. 1.在本类的成员方法中,访问本类的成员变量 2.在本类的成员方法中,访问本类的

随机推荐