前言
CLR(Common Language Runtime)支持兩種類型:值類型和引用類型周瞎。
關(guān)于這兩兄弟黄琼,很多程序員都不會(huì)去深究樊销。大部分也就是在面試前草草看兩眼。其實(shí)非常多的BUG和性能優(yōu)化都需要從這兩兄弟開(kāi)始下手
本篇僅記錄一些經(jīng)常被忽視的特性,有興趣的朋友歡迎在文章下方留言討論.
值類型
1.所有的值類型都是隱式密封脏款。
其目的是防止將值類型作為其它值類型或者引用類型的基類围苫。
具體表現(xiàn)為:
2.值類型不受垃圾回收器的控制
原因是絕大部分值類型儲(chǔ)存在棧里(值類型可以作為引用類型的字段存在堆里),而垃圾回收器只能管道堆撤师。
因此值類型的大量使用可以緩解托管堆的壓力剂府,并減少引用程序生存周期內(nèi)的垃圾回收次數(shù)!
總而言之剃盾,在開(kāi)發(fā)時(shí)應(yīng)優(yōu)先考慮值類型
3.值類型的定義方式
int i=5;//標(biāo)準(zhǔn)的定義方式
bool b=new bool();//忽悠‘小弟’專用的定義方式
所以:永遠(yuǎn)不要用是否可以new的的思維來(lái)區(qū)分值類型和引用類型周循!
有的人可能會(huì)覺(jué)得int i創(chuàng)建的是變量,而int i = new int()創(chuàng)建的是對(duì)象實(shí)例
,所以int i = new int()其實(shí)比int i更消耗內(nèi)存万俗。 這種說(shuō)法常按理說(shuō)是沒(méi)問(wèn)題的湾笛,可我們的宇宙第一編譯器具有無(wú)可匹敵的兼容性。所以在C#里這兩種寫(xiě)法其實(shí)幾乎沒(méi)有區(qū)別闰歪,因?yàn)樗麄冏罱K生成的IL代碼是一樣的嚎研。但是第二種寫(xiě)法比較繁雜且具有一定迷惑性,所以幾乎沒(méi)什么人用。
4.結(jié)構(gòu)體
結(jié)構(gòu)體也是值類型.你可以簡(jiǎn)單地認(rèn)為當(dāng)一個(gè)“類”被struct修改而不是class的時(shí)候临扮,那么這個(gè)“類”就是結(jié)構(gòu)體论矾。
//定義一個(gè)結(jié)構(gòu)體
public struct Val
{
public int MyInt;
}
a) 結(jié)構(gòu)體的使用方式杆勇。
Val yourInt;
Val myInt = new Val();
與其它值類型不同的是贪壳,上面兩種方式其實(shí)有一點(diǎn)小區(qū)別:第二種方式編譯器會(huì)認(rèn)為實(shí)例已初始化,而第一種編譯器會(huì)在編譯的時(shí)候?yàn)槠涑跏蓟ㄉ傻腎L是一致的)蚜退。以下代碼可以更清楚地進(jìn)行說(shuō)明:
b) 結(jié)構(gòu)體不可以自己寫(xiě)默認(rèn)的構(gòu)造器闰靴。如果要寫(xiě)構(gòu)造器,則必須把所有字段初始化一次钻注。效果如下:
引用類型
使用引用類型時(shí)蚂且,必須注意性能問(wèn)題。
a) 引用類型的內(nèi)存必須從托管堆分配幅恋,而其地址存在棧里杏死。
b) 托管堆里分配的每個(gè)對(duì)象都會(huì)有一些額外成員,這些成員必須初始化
c) 從托管堆分配對(duì)象時(shí)捆交,可能會(huì)強(qiáng)制執(zhí)行一次垃圾回收(現(xiàn)在應(yīng)該不存在這個(gè)顧慮了淑翼,沒(méi)深究過(guò))
值類型和引用類型的區(qū)別
1.值類型分配在棧上,引用類型分配在堆上品追。
在拷貝值類型的時(shí)候其實(shí)是把值類型的地址和內(nèi)存拷貝給了新的對(duì)象窒舟,
在拷貝引用類型的時(shí)候是把引用類型的地址拷貝給了新對(duì)象。也就是說(shuō)引用類型的新老對(duì)象其實(shí)是兩個(gè)地址一個(gè)內(nèi)存诵盼,當(dāng)你修改新對(duì)象的值的時(shí)候其實(shí)吧老對(duì)象的值也該了惠豺。以下代碼可以更直觀地表現(xiàn):
2.幾乎所有的引用類型都直接從System.Object繼承,而值類型則直接繼承System.Object的子類System.ValueType
3.值類型轉(zhuǎn)換為引用類型稱為裝箱风宁,引用類型轉(zhuǎn)換為值類型稱為拆箱
因?yàn)镃#所有的類型都繼承自O(shè)bject所以值類型和引用類型可以互轉(zhuǎn).而這一行為就被稱為裝箱或者拆箱.
裝箱:值類型=>Object=>引用類型 : 將值類型字段拷貝到托管堆上發(fā)生的內(nèi)存分配
拆箱:引用類型=>Object=>值類型 : 把托管堆上值類型數(shù)據(jù)傳遞到棧
為什么需要裝箱或者拆箱?
在實(shí)際開(kāi)發(fā)的時(shí)候經(jīng)常會(huì)遇到不確定入?yún)㈩愋突蛘叻祷仡愋偷那闆r.這個(gè)時(shí)候就可以直接把不確定的類型設(shè)置為Object,然后在使用時(shí)候再使用其具體類型.
但是這種操作是極其消耗內(nèi)存的.所以從C#0.2開(kāi)始我們有了泛型.
理論上一切你需要拆裝箱的操作都可以用泛型取代.
4.new對(duì)象操作不一定會(huì)在堆上開(kāi)辟內(nèi)存
在new一個(gè)引用類型時(shí)洁墙,C#會(huì)在堆上為對(duì)象開(kāi)辟一塊內(nèi)存,并返回該內(nèi)存的地址儲(chǔ)存在棧上戒财。 而在new一個(gè)結(jié)構(gòu)體時(shí)热监,編譯器會(huì)識(shí)別到它,并在棧上為其開(kāi)辟內(nèi)存(宇宙第一編譯器的基本操作)饮寞。