C#基礎(chǔ)知識(shí) 全面解析可空類型
引言:
C# 2.0 中還引入了可空類型,可空類型也是值類型,只是可空類型是包括null的值類型的,下面就介紹下C#2.0中對(duì)可空類型的支持具體有哪些內(nèi)容(最近一直都在思考如何來(lái)分享這篇文章的,因?yàn)閯傞_(kāi)始覺(jué)得可空類型使用過(guò)程中比較簡(jiǎn)單,覺(jué)得沒(méi)有講的必要,但是考慮到這個(gè)系列的完整性,決定還是嘮叨下吧,希望對(duì)一些不熟悉的人有幫助)。
一、為什么會(huì)有可空類型
如果朋友們看了我之前的分享,對(duì)于這一部分都不會(huì)陌生,因?yàn)槲乙话憬榻BC#特性經(jīng)常會(huì)以這樣的方式開(kāi)頭的, 因?yàn)槊總€(gè)特性都是有它出現(xiàn)的原因的(有一句佛語(yǔ)這是這么講的:萬(wàn)事皆有因,有因必有果),首先來(lái)說(shuō)說(shuō)這個(gè)因的(果當(dāng)然是新增加了可空類型這個(gè)新特性了。),當(dāng)我們?cè)谠O(shè)計(jì)數(shù)據(jù)庫(kù)的時(shí)候,我們可以設(shè)置數(shù)據(jù)庫(kù)字段允許為null值,如果數(shù)據(jù)庫(kù)字段是日期等這樣在C#語(yǔ)言是值類型時(shí),當(dāng)我們把數(shù)據(jù)庫(kù)表映射一個(gè)對(duì)象時(shí),此時(shí)Datetime類型在C# 語(yǔ)言中是不能為null的,如果這樣就會(huì)與數(shù)據(jù)庫(kù)的設(shè)計(jì)有所沖突,這樣開(kāi)發(fā)人員就會(huì)有這樣的需求了——值類型能不能也為可空類型的?同時(shí)微軟也看出了用戶有這樣的需求,所以微軟在C# 2.0中就新增加了一種類型——可空類型,即包含null值的值類型,這個(gè)也就是我理解的因了,介紹完因之后,當(dāng)然就是好好嘮叨下可空類型是個(gè)什么東西的了?
二、可空類型的介紹
可空類型也是值類型,只是它是包含null的一個(gè)值類型。我們可以像下面這樣表示可空類型(相信大家都不陌生):
int? nullable = null;
上面代碼 int? 就是可空的int類型(有人可能會(huì)這樣的疑問(wèn)的, 如果在C#1中我硬要讓一個(gè)值類型為一個(gè)可空類型怎么辦到呢?當(dāng)然這個(gè)在C#1之前也是有可以辦到的,只是會(huì)相當(dāng)麻煩,對(duì)于這個(gè)如果有興趣的朋友可以去刨下根),然而其實(shí) "?"這個(gè)修飾符只是C#提供的一個(gè)語(yǔ)法糖(所謂語(yǔ)法糖,就是C#提供的一種方便的形式,其實(shí)肯定沒(méi)有int? 這個(gè)類型,這個(gè)int?編譯器認(rèn)為的就是Nullable<int>類型,即可空類型),其實(shí)真真C# 2.0提供的可空類型是——Nullable<T>(這個(gè)T就是上專題介紹的泛型參數(shù),其中T只能為值類型,因?yàn)閺目煽疹愋偷亩x為:public struct Nullable<T> where T : struct)和Nullable。下面給出一段代碼來(lái)介紹可空類型的使用:
namespace 可空類型Demo
{
class Program
{
static void Main(string[] args)
{
// 下面代碼也可以這樣子定義int? value=1;
Nullable<int> value = 1;
Console.WriteLine("可空類型有值的輸出情況:");
Display(value);
Console.WriteLine();
Console.WriteLine();
value = new Nullable<int>();
Console.WriteLine("可空類型沒(méi)有值的輸出情況:");
Display(value);
Console.Read();
}
// 輸出方法,演示可空類型中的方法和屬性的使用
private static void Display(int? nullable)
{
// HasValue 屬性代表指示可空對(duì)象是否有值
// 在使用Value屬性時(shí)必須先判斷可空類型是否有值,
// 如果可空類型對(duì)象的HasValue返回false時(shí),將會(huì)引發(fā)InvalidOperationException異常
Console.WriteLine("可空類型是否有值:{0}", nullable.HasValue);
if (nullable.HasValue)
{
Console.WriteLine("值為: {0}", nullable.Value);
}
// GetValueOrDefault(代表如果可空對(duì)象有值,就用它的值返回,如果可空對(duì)象不包含值時(shí),使用默認(rèn)值0返回)相當(dāng)與下面的語(yǔ)句
// if (!nullable.HasValue)
// {
// result = d.Value;
// }
Console.WriteLine("GetValueorDefault():{0}", nullable.GetValueOrDefault());
// GetValueOrDefault(T)方法代表如果 HasValue 屬性為 true,則為 Value 屬性的值;否則為 defaultValue 參數(shù)值,即2。
Console.WriteLine("GetValueorDefalut重載方法使用:{0}", nullable.GetValueOrDefault(2));
// GetHashCode()代表如果 HasValue 屬性為 true,則為 Value 屬性返回的對(duì)象的哈希代碼;如果 HasValue 屬性為 false,則為零
Console.WriteLine("GetHashCode()方法的使用:{0}", nullable.GetHashCode());
}
}
}
輸出結(jié)果:
上面的演示代碼中都注釋,這里就不再解釋了,為了讓大家明白進(jìn)一步理解可空類型是值類型,下面貼出中間語(yǔ)言代碼截圖:
三、空合并操作符(?? 操作符)
??操作符也就是"空合并操作符",它代表的意思是兩個(gè)操作數(shù),如果左邊的數(shù)不為null時(shí),就返回左邊的數(shù),如果左邊的數(shù)為null,就返回右邊的數(shù),這個(gè)操作符可以用于可空類型,也可以用于引用類型,但是不能用于值類型(之所以不能應(yīng)用值類型(這里除了可空類型),因?yàn)??運(yùn)算符要對(duì)左邊的數(shù)與null進(jìn)行比較,然而值類型,不能與null類型比較,所以就不支持??運(yùn)算符),下面用一個(gè)例子來(lái)掩飾下??運(yùn)算符的使用(??這個(gè)運(yùn)算符可以方便我們?cè)O(shè)置默認(rèn)值,可以避免在代碼中寫(xiě)if, else語(yǔ)句,簡(jiǎn)單代碼數(shù)量,從而有利于閱讀。)
static void Main(string[] args)
{
Console.WriteLine("??運(yùn)算符的使用如下:");
NullcoalescingOperator();
Console.Read();
}
private static void NullcoalescingOperator()
{
int? nullable = null;
int? nullhasvalue = 1;
// ??和三目運(yùn)算符的功能差不多的
// 所以下面代碼等價(jià)于:
// x=nullable.HasValue?b.Value:12;
int x = nullable ?? 12;
// 此時(shí)nullhasvalue不能null,所以y的值為nullhasvalue.Value,即輸出1
int y = nullhasvalue ?? 123;
Console.WriteLine("可空類型沒(méi)有值的情況:{0}",x);
Console.WriteLine("可空類型有值的情況:{0}", y);
// 同時(shí)??運(yùn)算符也可以用于引用類型, 下面是引用類型的例子
Console.WriteLine();
string stringnotnull = "123";
string stringisnull = null;
// 下面的代碼等價(jià)于:
// (stringnotnull ==null)? "456" :stringnotnull
// 同時(shí)下面代碼也等價(jià)于:
// if(stringnotnull==null)
// {
// return "456";
// }
// else
// {
// return stringnotnull;
// }
// 從上面的等價(jià)代碼可以看出,有了??運(yùn)算符之后可以省略大量的if—else語(yǔ)句,這樣代碼少了, 自然可讀性就高了
string result = stringnotnull ?? "456";
string result2 = stringisnull ?? "12";
Console.WriteLine("引用類型不為null的情況:{0}", result);
Console.WriteLine("引用類型為null的情況:{0}", result2);
}
下面是運(yùn)行結(jié)果截圖:
四、可空類型的裝箱和拆箱
值類型存在裝箱和拆箱的過(guò)程,可空類型也屬于值類型,從而也有裝箱和拆箱的過(guò)程的, 這里先介紹下裝箱和拆箱的概念的, 裝箱指的的從值類型到引用類型的過(guò)程,拆箱當(dāng)然也就是裝箱的反過(guò)程,即從引用類型到值類型的過(guò)程(這里進(jìn)一步解釋下我理解的裝箱和拆箱,首先.Net中值類型是分配在堆棧上的,然而引用類型分配在托管堆上,裝箱過(guò)程就是把值類型的值從推棧上拷貝到托管堆上,然后推棧上存儲(chǔ)的是對(duì)托管堆上拷貝值的引用,然而拆箱就是把托管堆上的值拷貝到堆棧上.簡(jiǎn)單一句話概況,裝箱和拆箱就是一個(gè)值的拷貝的一個(gè)過(guò)程,就想搬家一樣,把東西從一個(gè)地方搬到另一個(gè)地方,對(duì)于深入的理解,大家可以參考下園中的博文.), 括號(hào)中是我理解的裝箱和拆箱的過(guò)程,下面就具體介紹下可空類型的裝箱和拆箱的:
當(dāng)把一個(gè)可空類型賦給一個(gè)引用類型變量時(shí),此時(shí)CLR 會(huì)對(duì)可空類型(Nullable<T>)對(duì)象進(jìn)行裝箱處理,首先CLR會(huì)檢測(cè)可空類型是否為null,如果為null,CLR則不進(jìn)行實(shí)際的裝箱操作(因?yàn)閚ull可以直接賦給一個(gè)引用類型變量),如果不為null,CLR會(huì)從可空類型對(duì)象中獲取值,并對(duì)該值進(jìn)行裝箱(這個(gè)過(guò)程就是值類型的裝箱過(guò)程了。),當(dāng)把一個(gè)已裝箱的值類型賦給一個(gè)可空類型變量時(shí),此時(shí)CLR會(huì)對(duì)已裝箱的值類型進(jìn)行拆箱處理,如果已裝箱值類型的引用為null,此時(shí)CLR會(huì)把可空類型設(shè)為null(如果覺(jué)得啰嗦,大家可以直接看下面的代碼,代碼中也會(huì)有詳細(xì)的注釋)。下面用一個(gè)示例來(lái)演示下可空類型的裝箱和拆箱的使用,這樣可以幫助大家更好的理解前面介紹的概念:
static void Main(string[] args)
{
//Console.WriteLine("??運(yùn)算符的使用如下:");
//NullcoalescingOperator();
Console.WriteLine("可空類型的裝箱和拆箱的使用如下:");
BoxedandUnboxed();
Console.Read();
}
// 可空類型裝箱和拆箱的演示
private static void BoxedandUnboxed()
{
// 定義一個(gè)可空類型對(duì)象nullable
Nullable<int> nullable = 5;
int? nullablewithoutvalue = null;
// 獲得可空對(duì)象的類型,此時(shí)返回的是System.Int32,而不是System.Nullable<System.Int32>,這點(diǎn)大家要特別注意下的
Console.WriteLine("獲取不為null的可空類型的類型為:{0}",nullable.GetType());
// 對(duì)于一個(gè)為null的類型調(diào)用方法時(shí)出現(xiàn)異常,所以一般對(duì)于引用類型的調(diào)用方法前,最好養(yǎng)成習(xí)慣先檢測(cè)下它是否為null
//Console.WriteLine("獲取為null的可空類型的類型為:{0}", nullablewithoutvalue.GetType());
// 將可空類型對(duì)象賦給引用類型obj,此時(shí)會(huì)發(fā)生裝箱操作,大家可以通過(guò)IL中的boxed 來(lái)證明
object obj = nullable;
// 獲得裝箱后引用類型的類型,此時(shí)輸出的仍然是System.Int32,而不是System.Nullable<System.Int32>
Console.WriteLine("獲得裝箱后obj 的類型:{0}", obj.GetType());
// 拆箱成非可空變量
int value = (int)obj;
Console.WriteLine("拆箱成非可空變量的情況為:{0}", value);
// 拆箱成可空變量
nullable = (int?)obj;
Console.WriteLine("拆箱成可空變量的情況為:{0}", nullable);
// 裝箱一個(gè)沒(méi)有值的可空類型的對(duì)象
obj = nullablewithoutvalue;
Console.WriteLine("對(duì)null的可空類型裝箱后obj 是否為null:{0}", obj==null);
// 拆箱成非可空變量,此時(shí)會(huì)拋出NullReferenceException異常,因?yàn)闆](méi)有值的可空類型裝箱后obj等于null,引用一個(gè)空地址
// 相當(dāng)于拆箱后把null值賦給一個(gè)int 類型的變量,此時(shí)當(dāng)然就會(huì)出現(xiàn)錯(cuò)誤了
//value = (int)obj;
//Console.WriteLine("一個(gè)沒(méi)有值的可空類型裝箱后,拆箱成非可空變量的情況為:{0}", value);
// 拆箱成可空變量
nullable = (int?)obj;
Console.WriteLine("一個(gè)沒(méi)有值的可空類型裝箱后,拆箱成可空變量是否為null:{0}", nullable == null);
}
上面代碼中都有注釋的, 而且代碼也比較簡(jiǎn)單, 這里就不解釋了, 其實(shí)可空類型的裝箱和拆箱操作大家可以就理解為非可空值類型的裝箱和拆箱的過(guò)程,只是對(duì)于非可空類型因?yàn)榘琻ull值,所以CLR會(huì)提前對(duì)它進(jìn)行檢查下它是否為空,為null就不不任何處理,如果不為null,就按照非可空值類型的裝箱和拆箱的過(guò)程來(lái)裝箱和拆箱。
五、小結(jié)
到這里本專題的介紹就完成了,本專題主要介紹了下可空類型以及可空類型相關(guān)的知識(shí),希望這篇文章可以幫助大家對(duì)可空類型的認(rèn)識(shí)可以更加全面,下一個(gè)專題將和大家介紹下匿名方法, 匿名方法也是Lambda表達(dá)式和Linq的一個(gè)鋪墊,然而它是C#2中被提出來(lái)了的, 從而可以看出Lambda和Linq在C# 3.0中被添加其實(shí)是微軟早在C# 2.0的時(shí)候就計(jì)劃好了的,早就計(jì)劃好了的(這也是我的推斷,然而我覺(jué)得為什么它不直接在把Lambda和Linq都放在C# 2中提出來(lái)的, 卻偏偏放在C# 3.0中提出,我理解原因有——1 覺(jué)得微軟當(dāng)時(shí)肯定是想一起提出的,但是后面發(fā)現(xiàn)這幾個(gè)新的特性提出后會(huì)對(duì)編譯器做比較大的改動(dòng),需要比較長(zhǎng)的時(shí)間來(lái)實(shí)現(xiàn),此時(shí)又怕用戶等不及了,覺(jué)得C#很多東西都沒(méi)有,所以微軟就先把做好了的部分先發(fā)布出來(lái),然而把Lambda和Linq放到C#3來(lái)提出。我推理覺(jué)得應(yīng)該是這樣的,所以C#的所有特性都是緊密相連的。)
相關(guān)文章
C#中自定義高精度Timer定時(shí)器的實(shí)例教程
這篇文章主要介紹了C#中自定義高精度Timer定時(shí)器的實(shí)例教程,多線程的Timer編寫(xiě)需要注意線程安全的問(wèn)題,需要的朋友可以參考下2016-04-04c#獲取當(dāng)前年的周數(shù)及當(dāng)前月的天數(shù)示例代碼
本篇文章主要是對(duì)c#獲取當(dāng)前年的周數(shù)及當(dāng)前月的天數(shù)示例代碼進(jìn)行了介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2014-01-01c# WPF設(shè)置軟件界面背景為MediaElement并播放視頻
這篇文章主要介紹了c# WPF如何設(shè)置軟件界面背景為MediaElement并播放視頻,幫助大家更好的理解和學(xué)習(xí)使用c#,感興趣的朋友可以了解下2021-03-03Entity Framework主從表數(shù)據(jù)加載方式
這篇文章介紹了Entity Framework主從表數(shù)據(jù)加載方式,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06Unity Shader實(shí)現(xiàn)描邊OutLine效果
這篇文章主要為大家詳細(xì)介紹了Unity Shader實(shí)現(xiàn)描邊OutLine效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-01-01