天天看點

[C#基礎知識系列]專題十:全面解析可空類型

 引言:

  C# 2.0 中還引入了可空類型,可空類型也是值類型,隻是可空類型是包括null的值類型的,下面就介紹下C#2.0中對可空類型的支援具體有哪些内容(最近一直都在思考如何來分享這篇文章的,因為剛開始覺得可空類型使用過程中比較簡單,覺得沒有講的必要,但是考慮到這個系列的完整性,決定還是唠叨下吧,希望對一些不熟悉的人有幫助)。

一、為什麼會有可空類型

   如果朋友們看了我之前的分享,對于這一部分都不會陌生,因為我一般介紹C#特性經常會以這樣的方式開頭的, 因為每個特性都是有它出現的原因的(有一句佛語這是這麼講的:萬事皆有因,有因必有果),首先來說說這個因的(果當然是新增加了可空類型這個新特性了。),當我們在設計資料庫的時候,我們可以設定資料庫字段允許為null值,如果資料庫字段是日期等這樣在C#語言是值類型時,當我們把資料庫表映射一個對象時,此時Datetime類型在C# 語言中是不能為null的,如果這樣就會與資料庫的設計有所沖突,這樣開發人員就會有這樣的需求了——值類型能不能也為可空類型的?同時微軟也看出了使用者有這樣的需求,是以微軟在C# 2.0中就新增加了一種類型——可空類型,即包含null值的值類型,這個也就是我了解的因了,介紹完因之後,當然就是好好唠叨下可空類型是個什麼東西的了?

二、可空類型的介紹

   可空類型也是值類型,隻是它是包含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。下面給出一段代碼來介紹可空類型的使用:

輸出結果:

上面的示範代碼中都注釋,這裡就不再解釋了,為了讓大家明白進一步了解可空類型是值類型,下面貼出中間語言代碼截圖:

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

  ??操作符也就是"空合并操作符",它代表的意思是兩個操作數,如果左邊的數不為null時,就傳回左邊的數,如果左邊的數為null,就傳回右邊的數,這個操作符可以用于可空類型,也可以用于引用類型,但是不能用于值類型(之是以不能應用值類型(這裡除了可空類型),因為??運算符要對左邊的數與null進行比較,然而值類型,不能與null類型比較,是以就不支援??運算符),下面用一個例子來掩飾下??運算符的使用(??這個運算符可以友善我們設定預設值,可以避免在代碼中寫if, else語句,簡單代碼數量,進而有利于閱讀。)

下面是運作結果截圖:

四、可空類型的裝箱和拆箱

   值類型存在裝箱和拆箱的過程,可空類型也屬于值類型,進而也有裝箱和拆箱的過程的, 這裡先介紹下裝箱和拆箱的概念的, 裝箱指的的從值類型到引用類型的過程,拆箱當然也就是裝箱的反過程,即從引用類型到值類型的過程(這裡進一步解釋下我了解的裝箱和拆箱,首先.Net中值類型是配置設定在堆棧上的,然而引用類型配置設定在托管堆上,裝箱過程就是把值類型的值從推棧上拷貝到托管堆上,然後推棧上存儲的是對托管堆上拷貝值的引用,然而拆箱就是把托管堆上的值拷貝到堆棧上.簡單一句話概況,裝箱和拆箱就是一個值的拷貝的一個過程,就想搬家一樣,把東西從一個地方搬到另一個地方,對于深入的了解,大家可以參考下園中的博文.), 括号中是我了解的裝箱和拆箱的過程,下面就具體介紹下可空類型的裝箱和拆箱的:

  當把一個可空類型賦給一個引用類型變量時,此時CLR 會對可空類型(Nullable<T>)對象進行裝箱處理,首先CLR會檢測可空類型是否為null,如果為null,CLR則不進行實際的裝箱操作(因為null可以直接賦給一個引用類型變量),如果不為null,CLR會從可空類型對象中擷取值,并對該值進行裝箱(這個過程就是值類型的裝箱過程了。),當把一個已裝箱的值類型賦給一個可空類型變量時,此時CLR會對已裝箱的值類型進行拆箱處理,如果已裝箱值類型的引用為null,此時CLR會把可空類型設為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#特性需要注意的地方歡迎大家給我留言。

     本文轉自LearningHard 51CTO部落格,原文連結:http://blog.51cto.com/learninghard/1067849,如需轉載請自行聯系原作者