可空類型、匿名方法和迭代器這三個優美的特性是在C#2.0裡面提出來的。 1、可空類型 當我們在使用資料庫的時候,會發現這樣的一個矛盾點:資料庫的欄位設置是允許為null的,比如日期的欄位,當你想把資料庫表映射為C#中的對象時會發現,DateTime類型在C#語言中是不能為null的! 1.1 簡介 ...
可空類型、匿名方法和迭代器這三個優美的特性是在C#2.0裡面提出來的。
1、可空類型
當我們在使用資料庫的時候,會發現這樣的一個矛盾點:資料庫的欄位設置是允許為null的,比如日期的欄位,當你想把資料庫表映射為C#中的對象時會發現,DateTime類型在C#語言中是不能為null的!
1.1 簡介
可空類型也是值類型,但它是包含null值的值類型:int? nullable=null;
int?就是可空的int類型。很明顯,這又是一個語法糖,肯定不會存在int?這樣的類型。對於編譯器而言,int?會被編譯成Nullable<int>類型,即可空類型。
C#2.0中提供的可空類型是Nullable<T>和Nullable(這個T就是一個泛型參數)。在C#中,定義是這樣的public struct Nullable<T> where T:struct ,很明顯,泛型只能是值類型。下麵的代碼演示了可空類型的使用方法:
1 static void Main(string[] args) 2 3 { 4 Nullable<int> value = 1;//int? value=1也可以 5 Console.WriteLine("可空類型有值的輸出情況:"); 6 Display(value); 7 Console.WriteLine(); 8 9 value = new Nullable<int>(); 10 Console.WriteLine("可空類型沒有值的輸出情況:"); 11 Display(value); 12 Console.ReadKey(); 13 14 } 15 16 private static void Display(int? value) 17 { 18 Console.WriteLine("可空類型是否有值:{0}",value.HasValue); 19 if (value.HasValue) 20 { 21 Console.WriteLine("值為:{0}",value.Value); 22 } 23 //如果可空類型有值,則返回Value屬性的值,否則就返回預設值 24 Console.WriteLine("GetValueorDefault():{0}",value.GetValueOrDefault()); 25 //如果可空類型有值,則返回Value屬性的值,否則就返回2 26 Console.WriteLine("GetValueorDefault():{0}", value.GetValueOrDefault(2)); 27 //如果HasValue屬性為true,則Value屬性返回對象的哈希代碼,否則為0 28 Console.WriteLine("GetHashCode()方法的使用:{0}",value.GetHashCode()); 29 }
1.2 空合併操作符
即為??操作符,它會對左右兩個操作數進行判斷,如果左邊的數不為null,就返回左邊的數;如果左邊的數為null,就返回右邊的數。這個操作符可用於可空類型,也可以用於引用類型,但是不能用於值類型。例如:
1 string stringnotnull="123" 2 string stringisnull=null; 3 string result=stringnotnull ?? "456"; 4 string result=stringisnull ?? "12";
以上的代碼運行結果為:result=“123”;result2=“12”。
1.3 可空類型的裝箱與拆箱操作
既然值類型存在著裝箱和拆箱的過程,而可空類型屬於值類型,那麼它自然也就存在裝箱和拆箱操作,下麵我們就來看看可空類型的裝箱和拆箱的過程。
當把一個可空類型賦給引用類型變數時,CLR會對可空類型(Nullable<T>)對象進行裝箱處理。CLR首先檢測可空類型是否為null。如果有null,CLR將不會進行實際的裝箱操作(因為null可以直接賦值給一個引用類型變數);如果不為null,CLR則從可空類型對象中獲取值,並對該值進行裝箱(即值類型的裝箱過程)。
當把一個已裝箱的值類型賦值給可空類型變數時,CLR會對已裝箱的值類型進行拆箱處理。如果已裝箱值類型的引用為null,則CLR會把可空類型也設為null。
1 static void Main(string[] args) 2 { 3 Console.WriteLine("可空類型的裝箱和拆箱的使用如下:"); 4 BoxAndUnbox(); 5 Console.ReadKey(); 6 } 7 8 private static void BoxAndUnbox() 9 { 10 Nullable<int> nullable = 5; 11 int? nullablewithoutvalue = null; 12 Console.WriteLine("讀取不為null的可空類型的類型為{0}",nullable.GetType()); 13 //出現NullReferenceException的異常 14 //Console.WriteLine("讀取為null的可空類型的類型為{0}", nullablewithoutvalue.GetType()); 15 object obj = nullable; 16 Console.WriteLine("獲得裝箱後obj的類型:{0}",obj.GetType()); 17 18 int value = (int) obj; 19 nullable = (int?) obj; 20 21 //對一個沒有值的可空類型的對象進行裝箱操作 22 obj = nullablewithoutvalue; 23 Console.WriteLine("對null的可空類型裝箱後obj是否為null:{0}",obj==null); 24 25 //拆箱一定要為可空類型 26 nullable = (int?) obj; 27 }
運行的結果如下:
由以上的結果可以得知:
(1)通過GetType方法來獲得賦值的可空類型時,返回的將是賦值的類型,在前面的的代碼中即是System.Int32,而不是System.Nullable<System.Int32>類型。
(2)對已賦值的可空類型裝箱後,如果使用GetType函數去獲得裝箱後的引用類型,輸出的將仍然是賦值的類型,在前面的代碼中即為System.Int32。
(3)如果把一個沒有值的可空類型裝箱之後再拆箱,不能拆箱為非可空類型的值類型,否則會拋出NullReferenceException異常。因為沒有值的可空類型裝箱後obj等於null,即引用一個空地址,如果拆箱為非可空類型的值類型,相當於把null賦值給一個int型的變數,而int類型屬性值類型,不能被賦值為null,因此會出現異常。
(4)還有一點必須要註意:沒有值的可空類型在調用GetType函數之前,編譯器會對可空類型進行裝箱操作,使其變為null,即空引用。所以之後再調用GetType函數時,就會拋出空引用異常了。