香蕉视频app

Keep on going never give up.

Let's Go

C# 學習筆記(56)可空類型

C#Lonely2019-06-15 19:00:46109次3條

可空類型也是值類型,它是包含null值的值類型。例如:int? num=null; int?就是可空的int類型。“?”修飾符只是C#提供的一個語法糖,所謂語法糖,就是C#提供的一種方便的表示形式。C#中肯定沒有int?這個類型,對于編譯器而言,int?會被編譯成Nullable<int>類型,即可空類型。C#2.0中提供的可空類型是Nullable<T>和Nullable(這個T就是泛型參數,由于可空類型的定義是public struct Nullable<T> where T:struct,T只能為值類型)。

Nullable<int> value = null;
//上面的代碼也可以這樣定義
int? num = 500;
//HasValue屬性指示可空對象是否有值
Console.WriteLine(value.HasValue); //false
Console.WriteLine(num.HasValue); //true

//GetValueOrDefault() 代表如果可空對象有值,就用它的值返回;如果可空對象不包含值,則返回默認值0
Console.WriteLine(value.GetValueOrDefault()); //輸出0
Console.WriteLine(num.GetValueOrDefault()); //輸出500

//GetValueOrDefault(T) 方法代表如果HasValue屬性為true,則返回value屬性的值,否則返回為參數的值
Console.WriteLine(value.GetValueOrDefault(200)); //輸出了200

//GetHashCode() 代表如果HasValue屬性為true,則value屬性返回對象的哈希代碼,如果HasValue屬性為false,則返回0
Console.WriteLine(num.GetHashCode()); //輸出了500
Console.ReadKey();

前面我們講到,可空類型也是值類型,這個結論是有理有據的,其依據就是可空類型的IL代碼,如下圖:

image.png


空合并操作符

空合并操作符即??操作符,它會對左右兩個操作數進行判斷:如果左邊的數不為null,就返回左邊的數;如果左邊的數為null,就返回右邊的數。這個操作符可以用于可空類型和引用類型,但不能用于值類型。(因為??運算符會將其左邊的數與null進行比較,但除了可空類型外,其他的值類型都是不能與null類型進行比較的,所以??運算符不能應用與值類型)。

Nullable<int> n1 = null;          
int? n2 = 500;
Console.WriteLine(n1 ?? n2); //輸出了500
// ??與三元運算符功能差不多
int n = n1.HasValue ? n1.Value : 500; //n為500

//同時??運算符也可以用于引用類型
string s = "你不是真正的快樂";
string str = null;
Console.WriteLine(s??str);

//上面代碼等價于:
Console.WriteLine(s != null ? s : str);
//也等價于:(偽碼)
//if (s != null)
//    return s;
//else
//    return str;
Console.ReadKey();

從以上代碼可以看出,使用??運算符可以很方便地設置默認值,避免了通過if和else語句來判斷,從而簡化了代碼行數,提高了代碼的可讀性。


可空類型的裝箱和拆箱操作

香蕉视频app 既然值類型存在裝箱和拆箱的過程,而可空類型屬于值類型,那么它自然也就存在裝箱和拆箱的操作了。下面我們一起來看看可空類型的裝箱和拆箱的過程。

當一個可空類型賦值給引用類型變量時,CLR會對可空類型(Nullable<T>)對象進行裝箱處理。CLR首先會檢測可空類型是否為null,如果為null,CLR將不會進行實際的裝箱操作(因為null可以賦值給一個引用類型變量);如果不為null,CLR則從可空類型對象中獲取值,并對該值進行裝箱(即值類型的裝箱過程)。

當把一個已裝箱的值類型賦值給可空類型變量時,CLR會對已裝箱的值類型進行拆箱處理。如果已裝箱值類型的引用為null,則CLR會把可空類型也設為null。

Nullable<int> n1 = null;
int? n2 = 500;
//獲得可空對象的類型,此時返回System.Int32,而不是System.Nullable<System.Int32>,這點注意
Console.WriteLine("獲取不為null的可空對象的類型:" + n2.GetType()); //輸出System.Int32
//對一個為null的類型調用方法時將出現異常,所以一般對引用類型調用方法前,最好先檢測下它是否為null
//Console.WriteLine("獲取為null的可空對象的類型:" + n1.GetType()); //這句將報異常

//將不為null的可空類型對象賦值給引用類型,此時發生裝箱操作,可通過IL證明
object obj = n2;
Console.WriteLine("獲得裝箱后引用類型的類型:" + obj.GetType()); //輸出System.Int32

//拆箱成非可空變量
int num = (int)obj;
Console.WriteLine(num); //500

//拆箱成可空變量
n1 = (int?)obj;
Console.WriteLine(n1); //500

int? n3 = null;
obj = n3; //對一個沒有值的可空類型對象進行裝箱操作
Console.WriteLine(obj == null); //true

n3 = (int?)obj; //拆箱成可空變量
Console.WriteLine(n3 == null); //true

//拆箱成非可空變量,此時會拋異常,因為沒有值的可空類型裝箱后obj等于null,引用了一個空地址,相當于拆箱后把null賦值給一個int類型的變量,所以會拋異常
//int n4 = (int)obj;

Console.ReadKey();

對于可空類型,我們需要注意的地方有以下幾點:

香蕉视频app 1、通過GetType方法來獲取賦值的可空類型時,返回的將是賦值的類型。

2、對已賦值的可空類型裝箱后,如果使用GetTpye函數去獲取裝箱后的引用類型,輸出的將仍然是已賦值的的類型。

3、如果把一個沒有值的可空類型裝箱之后再拆箱,不能拆箱為非可空類型的值類型,否則會拋出NullReferenceException異常。

香蕉视频app 最后還有一點,需要特別注意,對于沒有值的可空類型調用函數時會拋出引用異常,那么為什么沒有值的可空類型仍然可以訪問HasValue屬性呢?

香蕉视频app 原因在于,可空類型是包含null值的可空類型,對于向可空類型賦值這項操作來說,null是一個有效的值類型。而向引用類型賦值null值則表示為空引用,表示不會指向托管堆中的任何對象,所以可以訪問HasValue屬性。沒有值的可空類型在調用GetType函數前,編譯器會對可空類型進行裝箱操作,使其變為null,即空引用。所以之后再調用GetType函數時,就會拋出空引用異常了。


暗錨,解決錨點偏移

文章評論

    嘿,來試試登錄吧!