分享

一个值在什么情况下不是一个值呢?----------关于C#中的可空类型

 昵称10504424 2013-03-25
可空类型由C# 2.0版本引入泛型时同时引入。

          在C#1.0版本中,值类型是不允许为NULL的。但有几种方案可由程序员自己实现可空的值类型。

          引用类型的空值意味着不引用任何对象。内存中用全零表示NULL,但存储方式上和其他引用无任何差别,即没有多任何一个bit用来标记该引用是否为NULL。这意味全零地址同其他地址一样是一个真实的地址,但不能用于一个真正的引用。但这并不是问题,因为内存地址那么多,完全没必要非要用全零来引用一个活动对象,如果活动对象太多,以至于不得不用全零地址,恐怕内存早就撑爆了。所以引用类型选择牺牲一个潜在的地址,来换取能使用NULL值的便利。

          值在概念上只有一个字节构成,可以将0~255存储到那个字节。若超出该范围,读取到的就是垃圾数据。每个值能处理256个“普通”的值,若要再加一个NULL值,就要为每个值类型设置一个额外的标志位,用来判断是真正的值还是NULL。即将值类型改为 1位标志位+7位数据位,则只能表示0~127这128个值。这样,内存的消耗将急剧增大,更不用说每次想要用值类型时,都要检查标志位。所以,对于值类型,选择牺牲NULL值,来换取真正的值的完整的位模式。

          但在开发过程中,经常遇到需要使用为NULL的值类型,包括但不限于数据库中日期,整数等都需要使用NULL。在C# 1.0中,解决值类型的可空性,有以下3种方案。

一、魔值:  该方案主要做为DateTime的解决方案,在数据库中,很少有人希望自己的数据中真正的包含某个数据,例如公元1900年01月01日。0时0分0秒。 所以我们可以牺牲一个值(通常是DateTime.MinValue)来表示空值,这个值成为魔值。使用魔值的好处是不用浪费内存,也不用重新构造新的数据类型。但设计不是那么优雅。让人感觉别扭。

二、引用类型包装:  即用object做为变量类型,并根据需要进行拆箱和装箱。复杂一些还可以声明一些隐式转换。该方法允许直接使用NULL,但需在堆上创建对象,频繁使用将会造成垃圾回收困难。

三、值类型包装:  即将一个bool类型的值(用来表示是否为NULL)和一个表示value的值(用来表示真正的值)包装进一个struct值类型。该方案与方案二相似,区别是使用了值类型,从而避免了垃圾回收的问题。缺点是要为每个想处理的类型都创建一个新的可空类型。该方案实际上就是C# 2.0中可空类型的工作方式。

    

    C# 2.0中 引入了System.Nullable<T>和System.Nullable。下面陈述省去域名。

           Nullable为一个静态方法类。提供了3个方法Compare<T>,Equals<T>,GetUnderlyingType。这3个方法完全可以由其他类型很好的实现,从而省去这个类,但由于历史遗留问题,该类不能被擦去,从而保留至今。

           Nullable<T>是即是可空泛型结构。对该泛型结构的基本用法此处不再赘述。下面仅讨论其在装箱和拆箱时的一些特殊行为。

           Nullable<T>是一个struct----一个值类型。这意味着如果把它转换成引用就要对其进行装箱。

    Nullable<T>的实例要么装箱成空引用。要么装箱成值类型T的一个已装箱值。永远不可能装箱成一个"装箱的可空值类型T"(即对Nullable<T>装箱后的object),因为不存在该类型。举个例子:int 装箱成obejct后,引用的是int。 那么我们要用可空的int时,构造一个Nullable<int>,然后对其装箱,当其非空时,此处装箱为对一个普通int值类型进行装箱的结果,而不是装箱为对一个可空int值类型Nullable<int>进行装箱的结果。当其为空的时候,则装箱成空引用,即上文提到的引用全零地址。

    已装箱的值可以拆箱成普通类型,或者对应的可空类型。拆箱一个空引用时,如果拆箱成普通类型,则会抛出一个NullReferenceException;但如果拆箱成恰当的可空类型,就会拆箱成没有值的一个可空类型的实例。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多