[C#基础知识系列]专题十:全面解析可空类型51CTO博客 - AG环亚娱乐

[C#基础知识系列]专题十:全面解析可空类型51CTO博客

2019年03月10日09时42分09秒 | 作者: 金鹏 | 标签: 类型,可空,装箱 | 浏览: 3004

 导言:

  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(stringnotnullnull)     // {     //  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}", objnull);      // 拆箱成非可空变量,此刻会抛出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#的一切特性都是严密相连的。)

 留意:有网友提醒了我一个需求首要的点,所以放在这儿弥补下,假如仔细的朋友或许会发现,当可空类型为null时,此刻仍是能够调用HasValue特点,即此刻的回来值为false,或许就会有这样的疑问的,为什么目标为null了还能够调用特点,此刻不会呈现NullReferenceException反常吗?其实关于这个问题我之前也觉得古怪的,后边经过查找也知道了原因了——首要,可空类型是值类型,当可空类型为null时,此刻可空类型并不是null(引证类型中的null),关于可空类型null这个是一个有用的值类型的,所以它调用HasValue不会抛出反常的(值类型时不或许为null的,可空类型为null的,此刻null与引证类型是不相同的,这点咱们有必要清晰)。一同这个问题也使我加深了对可空类型的了解,这儿共享出来能够让咱们进一步了解可空类型,假如咱们有什么定见和C#特性需求留意的当地欢迎咱们给我留言。

 

版权声明
本文来源于网络,版权归原作者所有,其内容与观点不代表AG环亚娱乐立场。转载文章仅为传播更有价值的信息,如采编人员采编有误或者版权原因,请与我们联系,我们核实后立即修改或删除。

猜您喜欢的文章