博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
《CLR via C#》笔记——可空值类型
阅读量:5936 次
发布时间:2019-06-19

本文共 5479 字,大约阅读时间需要 18 分钟。

一,可空值类型

  顾名思义,这种类型支持null值。在FCL中被定义为System.Nullable<T>类型。这是一个结构体类型(struct),所以,不要认为System.Nullable<T>是一个引用类型,它仍然是一个值类型

要使用可空值类型,在代码中可以这样写:

Nullable
x = 10; Nullable
y = null; Console.WriteLine("x: HasValue={0} Value={1}", x.HasValue, x.Value); Console.WriteLine("y: HasValue={0} Value={1}", y.HasValue, y.GetValueOrDefault());

得到的输出如下:

x: HasValue=True Value=10y: HasValue=False Value=0

二,C#对可空值类型的支持

    C#开发团队希望将可空值类型集成到C#语言中,为此,C#提供了一个更简单清晰的语法来处理可空值类型。可以使用类型加?号的方式来表示一个可空值类型。如下:

Int32? x = 10;            Int32? y = null;

在C#中,Int32?等价于System.Nullable<Int32>。但是C#在此基础上更进一步,允许开发人员在可空类型上执行转换和转型。转换:如Int32? ⇒ Int32。转型:如Int32? ⇒ Double?。

Int32? a = 5; //从非可空Int32转换成Nullable
Int32? b = null; //从‘null’隐式转换为Nullable
Int32 c = (Int32)a; //从Nullable
显示转换为Int32 Double? d = 5; //从Int32转型为Nullable
(d是double值5.0) Double? e = b; //从Nullable
转型为Nullable
(e是null)

C#还允许向可空实例应用操作符。下面是一些例子:

Int32? a = 5;            Int32? b = null;            //一元操作符(+ ++ - -- ! ~)            a++;    //a = 6            b = -b; //b = null            //二元操作符(+ - * / % & | ^ << >>)            a = a + 3;  //a = 9            b = b * 3; //b = null            Int32 c = (Int32)a;            //相等操作符(== !=)            if (a == null) { /* no */ } else { /* yes */}            if (b == null) { /* yes */ } else { /* no */}            if (a != null) { /* yes */}            //比较操作符(< > <= >=)            if (a < b) { /* no */ } else { /* yes */}

●一元操作符(+,++,-,--,!,~) 操作数是null,结果就是null。

●二元操作符(+,-,*,/,%,&,|,^,>>,<<) 两个操作数中任何一个是null,结果就是null。但有一个例外,它发生在&和|操作符应用于Boolean?操作数时。在这种情况下,这两个操作符的行为和SQL的三值逻辑是一致的。(为了便于理解,可以将这里的null理解为UnKnown,一个非null操作数遇到一个null(UnKnown)操作数,具体要看两个数能不能确定一个值,如果不能确定就是null(UnKnown)。比如A是true,B是null,那么A&B的结果是什么呢?由于B是不确定的,可以是false或true,那么A&B的结果可能是false,也可能是true,这样一来,A&B的结果就被认为是不确定的,于是就是null。而A|B的结果,不管B是false还是true,由于A是true,可以推断出A|B的结果就是True。)

●相等符操作符(==,!=)两个操作符都是null,两者相等。一个操作数是null,则两者不相等。两个操作数都不是null,就比较值来判断是否相等。

●关系操作符(<,>,<=,>=) 两个操作符任何一个是null,结果就是false。两个操作数都不是null,就比较值。

三,C#的空接合操作符(??

    C#提供了一个所谓的“空接合操作符”(null-coalescing operator),即??操作符,它要获取两个操作数。假如左边的操作数不是null,就返回这个操作数的值。如果左边的操作数是null,就返回右边这个操作数的值。利用空接合操作符可以方便的设置变量的默认值。它的另一个妙处在于,它既能用于引用类型,也能用于可空值类型。下面的代码演示了这种用法:

Int32? b = null;            Int32? x = b ?? 123;            //上面折行等价于            //x = b.HasValue ? b.Value : 123;            string fileName = GetFileName() ?? "untitled";            //上面这行等价于            //string temp = GetFileName();            //fileName = temp != null ? temp : "untitled";

有人认为??操作符只不过是三元操作符?:的“语法糖”而已。事实上,??操作符提供了重大的语法上的改进,第一个改进就是更好的支持表达式。

Func
f = () => SomeMethod() ?? "unknown";

相比下一行代码,上述代码更容易理解,下面的代码要求进行变量赋值,而且一个语句还搞不定:

Func
f = () => { var temp = SomeMethod(); return temp != null ? temp : "unknown"; };

第二个改进是??在复合的情形中更好用。例如:

string s = SomeMethod1() ?? SomeMethod2() ?? "unknown";

它比下面这一堆代码更容易阅读和理解。

string s;            var sm1 = SomeMethod1();            if (sm1 != null) s = sm1;            else            {                var sm2 = SomeMethod2();                if (sm2 != null) s = sm2;                else                    s = "unknown";            }

四,CLR对可空值类型的特殊支持

4.1,可空值类型的装箱

CLR对一个Nullable<T>实例装箱时,他会检查它是否为null。如果是,则不实际装箱任何东西,并返回null值。如果实例不为null,CLR从可空实例中取出值,并对其进行装箱。

Int32? n = null;            object o = n; //o为null            Console.WriteLine("o is null = {0}", o == null);//true            n = 5;            o = n; //o引用了一个已装箱的Int32            Console.WriteLine("o' type {0}", o.GetType());//"System.Int32"

4.2,可空值类型的拆箱

CLR允许将一个已装箱的值类型T拆箱成为一个T或者一个Nullable<T>。如果对已装箱的类型的引用是null,而且要把它拆箱为一个Nullable<T>,那么CLR会将Nullable<T>的值设为null。下面的代码进行了演示:

object o = 5;            //它拆箱为一个Nullable
和一个Int32 Int32? a = (Int32?)o; Int32 b = (Int32)o; //创建初始化为null的一个引用 o = null; //把它拆箱为一个Nullable
和一个Int32 a = (Int32?)o; // a = null b = (Int32)o; //NullReferenceException

4.3,通过可空值类型调用GetType

在一个Nullable<T>对象上调用GetType时,CLR实际会“撒谎”说类型是T,而不是Nullable<T>。下面的代码演示了这一行为:

Int32 x = 5;            //下面这行会显示“System.Int32”,而非“System.Nullable
Console.WriteLine(x.GetType());

4.4,通过可空值类型调用接口

下面的代码,将一个Nullable<Int32>类型的变量转型为一个IComparable<Int32>接口类型,但Nullable<T>不像Int32那样实现了IComparable<Int32>接口。C#编译器允许这样的代码通过编译,而且CLR的验证器认为这是可以验证的,从而允许我们使用一种更简洁的语法:

Int32? a = 5;            Int32 result = ((IComparable)a).CompareTo(5);//能通过编译和运行            Console.WriteLine(result);//0

假如CLR没有提供这个支持,就要写非常繁琐的代码。首先必须转型成Int32,再转型成接口以发出调用:

Int32 result = ((IComparable)(Int32)a).CompareTo(5);//这太繁琐了

4.5,语法糖陷阱

int? b = null;            int? c = new int?();

上面两句代码是等价的,int? b = null;被编译后会变成int? b = new int?();

int? 可空类型是值类型,这里的null值并不等同于引用类型的null,这里要特别的注意。

可空类型null值的比较只是编译器帮我们做了额外的工作,不要被它欺骗了。如下面的判断:

if (a == null)            { }            if (a.HasValue)            { }

不要天真的以为a==null这句和我们平常写的一个对象为null的判断是等价的,那你就错了。

真实的情况是,上面的代码被编译后被翻译成了:

if (!a.HasValue)    {    }    if (a.HasValue)    {    }

a==null被编译成了a.HasValue。这也就解释了为什么引用类型的变量为null时,不能够调用其成员,会报一个NullReferenceException的异常。

而可空类型即使你对它赋值为null,也可以调用它的成员,如a.HasValue,a.Equals等方法。

这全是C#的编译器的语法糖。可空类型是值类型,它永远会有一个结构,而不会为空引用null。

 

转载于:https://www.cnblogs.com/xiashengwang/archive/2012/07/20/2601559.html

你可能感兴趣的文章
day18 Set集合
查看>>
Oracle event之db file read
查看>>
ORA 00600 [ktrexc_1]
查看>>
Docker 安装
查看>>
查询文件系统容量与每个目录的容量
查看>>
角度与弧度的转换 - 三角学
查看>>
接口规范 3.应用相关接口
查看>>
traceroute命令
查看>>
zabbix接入百度告警详细攻略
查看>>
在Exchange 2010邮箱迁移时有用的命令行
查看>>
smarty学习
查看>>
11.python并发入门(part11 进程同步锁,以及进程池,以及callback的概念)
查看>>
GitHub简单仓库管理
查看>>
我的友情链接
查看>>
虚拟化技术对比
查看>>
angular的编辑器tinymce
查看>>
mybatis之三:与spring集成
查看>>
第二阶段团队进展报告(1)
查看>>
如何确定一个网站是用Wordpress开发的
查看>>
我的友情链接
查看>>