可空值類型和?運算符
談到運算符,大家一定很熟悉,但是對所有的運算符都能掌握嗎? 看了下面代碼再回答。
1 Nullable<Int32> count = 3; 2 3 int? i = 1; 4 5 bool? flag = false; 6 7 bool hasValue = flag ?? false;
相信在大多數情況下,對第三行和第7行的使用方法比較少。他們究竟代表啥含義,int? 和 int 有什么區別, “??”運算符是什么意思?
這個問題就需要提到C#中可空值類型(Nullable),顧名思義,他就是一個可以為null的值類型,嗯,是為null的值類型,沒聽錯。不是值類型不能為空,為啥要引入一個可為空的值類型呢?這就需要涉及到實際開發中碰到的問題來看。例如,設計一個數據庫時,開獎一個列的數據類型定義為32位整數,並且可以映射到FCL的Int32。但是有些情況下,數據庫列為空,這樣從數據庫讀出來的數據在映射為FCL的數據類型時該怎么辦?CLR中不能講一個Int32表示成null。
這樣引入可空值類型就是有必要的.上面代碼中第一行就是可空值類型的使用,他是一個泛型類
public struct Nullable<T> where T : struct { }
T 被限定為struct,即值類型,如果是引用類型就沒必要使用可空值類型,可以直接賦值為null。那么第三行的代碼是怎么回事呢? 這就是用"?"來表示的可空值類型,Nullable<Int32> 等同於 Int32? ,個人觀點來說Nullable<T>這種寫法更具有直觀性。
從上面看出可控制類型是個類,因此Nullable<Int32>和Int32 是不能直接來用=賦值的(編譯器報錯),那么他們在使用過程中就進行類型轉換,例如
1 int x = 9; 2 int? a = x; //正確 3 int? z = (int?)x;//正確 4 //int y = a; //Error Cannot implicitly convert type 'int?' to 'int' 5 int y = (int)a;//正確
第二行代碼中賦值是進行了隱式類型轉換,編譯器沒有報錯,第三行進行顯式類型轉換也沒有問題。但是第四行就會報錯,需要強制類型轉換。那么有類型轉換,那么肯定會聯想到裝箱和拆箱,那么編譯器是怎么裝箱和拆箱的呢?
裝箱:當CLR對一個Nullable<T>實例進行裝箱時,首先檢查他是否為null,如果是,CRL不進行任何操作,直接返回null。如果實例不為null,CLR對可空值實例中的值進行裝箱 。
Int32? n = null; object o = n;
拆箱:可以將一個已裝箱的值類型T拆箱成一個T或者一個Nullable<T>, 如果已裝箱的值類型引用為null,就拆箱成一個Nullable<T>,
1 object obj1 = null; 2 object obj2 = 1; 3 int? o1 = (int?)obj1; 4 int? o2 = (int?)obj2; 5 int i1 = (int)obj1;//NullException 6 int i2 = (int)obj2;
Nullable<T>的類型
下面我們來看一下Nullable<T>的類型,可以使用代碼查看,
1 2 Nullable<Int32> count = 3; 3 Console.WriteLine(count.GetType());//"System.Int32"
為什么Nullable<Int32>的類型和Int32的類型相同呢?如果他們真的相同,為什么不能直接賦值,要進行強制轉換呢?這就是CLR對我們撒謊了。
??運算符
最后我們了解一下運算符“??”(null-coalescing operator 空接合運算符),他是一個類似於?:運算符的的運算符,a??b 表示如果a 不等於null,則返回a, 否則返回b。對於使用不使用,什么時候使用 仁者見仁智者見智,所以不多說,只給出一個例子。
1 Int32? temp = null; 2 Int32 temp1 = temp ?? 123; //Equals temp1 = temp.HasValue ? temp.Value:123;