C#基礎(chǔ)之類(lèi)型和變量

前言

CLR(Common Language Runtime)支持兩種類(lèi)型:值類(lèi)型和引用類(lèi)型。

關(guān)于這兩兄弟,很多程序員都不會(huì)去深究。大部分也就是在面試前草草看兩眼。其實(shí)非常多的BUG和性能優(yōu)化都需要從這兩兄弟開(kāi)始下手

本篇僅記錄一些經(jīng)常被忽視的特性,有興趣的朋友歡迎在文章下方留言討論.

值類(lèi)型

1.所有的值類(lèi)型都是隱式密封。

其目的是防止將值類(lèi)型作為其它值類(lèi)型或者引用類(lèi)型的基類(lèi)。
具體表現(xiàn)為:


隱式密封

2.值類(lèi)型不受垃圾回收器的控制

原因是絕大部分值類(lèi)型儲(chǔ)存在棧里(值類(lèi)型可以作為引用類(lèi)型的字段存在堆里),而垃圾回收器只能管道堆。
因此值類(lèi)型的大量使用可以緩解托管堆的壓力,并減少引用程序生存周期內(nèi)的垃圾回收次數(shù)!
總而言之,在開(kāi)發(fā)時(shí)應(yīng)優(yōu)先考慮值類(lèi)型

3.值類(lèi)型的定義方式

int i=5;//標(biāo)準(zhǔn)的定義方式
bool b=new bool();//忽悠‘小弟’專(zhuān)用的定義方式

所以:永遠(yuǎn)不要用是否可以new的的思維來(lái)區(qū)分值類(lèi)型和引用類(lèi)型!
有的人可能會(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)體也是值類(lèi)型.你可以簡(jiǎn)單地認(rèn)為當(dāng)一個(gè)“類(lèi)”被struct修改而不是class的時(shí)候,那么這個(gè)“類(lèi)”就是結(jié)構(gòu)體。

//定義一個(gè)結(jié)構(gòu)體
public struct Val
{
    public int MyInt;
}

a) 結(jié)構(gòu)體的使用方式。

Val yourInt; 
Val myInt = new Val();

與其它值類(lèi)型不同的是,上面兩種方式其實(shí)有一點(diǎn)小區(qū)別:第二種方式編譯器會(huì)認(rèn)為實(shí)例已初始化,而第一種編譯器會(huì)在編譯的時(shí)候?yàn)槠涑跏蓟ㄉ傻腎L是一致的)。以下代碼可以更清楚地進(jìn)行說(shuō)明:

Struct

b) 結(jié)構(gòu)體不可以自己寫(xiě)默認(rèn)的構(gòu)造器。如果要寫(xiě)構(gòu)造器,則必須把所有字段初始化一次。效果如下:


手寫(xiě)默認(rèn)構(gòu)造器.png

構(gòu)造器里初始化部分字段.png

引用類(lèi)型

使用引用類(lèi)型時(shí),必須注意性能問(wèn)題。

a) 引用類(lèi)型的內(nèi)存必須從托管堆分配,而其地址存在棧里。
b) 托管堆里分配的每個(gè)對(duì)象都會(huì)有一些額外成員,這些成員必須初始化
c) 從托管堆分配對(duì)象時(shí),可能會(huì)強(qiáng)制執(zhí)行一次垃圾回收(現(xiàn)在應(yīng)該不存在這個(gè)顧慮了,沒(méi)深究過(guò))

值類(lèi)型和引用類(lèi)型的區(qū)別

1.值類(lèi)型分配在棧上,引用類(lèi)型分配在堆上。

值類(lèi)型和引用類(lèi)型

在拷貝值類(lèi)型的時(shí)候其實(shí)是把值類(lèi)型的地址和內(nèi)存拷貝給了新的對(duì)象,
在拷貝引用類(lèi)型的時(shí)候是把引用類(lèi)型的地址拷貝給了新對(duì)象。也就是說(shuō)引用類(lèi)型的新老對(duì)象其實(shí)是兩個(gè)地址一個(gè)內(nèi)存,當(dāng)你修改新對(duì)象的值的時(shí)候其實(shí)吧老對(duì)象的值也該了。以下代碼可以更直觀地表現(xiàn):


例子.png

2.幾乎所有的引用類(lèi)型都直接從System.Object繼承,而值類(lèi)型則直接繼承System.Object的子類(lèi)System.ValueType

3.值類(lèi)型轉(zhuǎn)換為引用類(lèi)型稱(chēng)為裝箱,引用類(lèi)型轉(zhuǎn)換為值類(lèi)型稱(chēng)為拆箱

因?yàn)镃#所有的類(lèi)型都繼承自O(shè)bject所以值類(lèi)型和引用類(lèi)型可以互轉(zhuǎn).而這一行為就被稱(chēng)為裝箱或者拆箱.
裝箱:值類(lèi)型=>Object=>引用類(lèi)型 : 將值類(lèi)型字段拷貝到托管堆上發(fā)生的內(nèi)存分配
拆箱:引用類(lèi)型=>Object=>值類(lèi)型 : 把托管堆上值類(lèi)型數(shù)據(jù)傳遞到棧

為什么需要裝箱或者拆箱?
在實(shí)際開(kāi)發(fā)的時(shí)候經(jīng)常會(huì)遇到不確定入?yún)㈩?lèi)型或者返回類(lèi)型的情況.這個(gè)時(shí)候就可以直接把不確定的類(lèi)型設(shè)置為Object,然后在使用時(shí)候再使用其具體類(lèi)型.

但是這種操作是極其消耗內(nèi)存的.所以從C#0.2開(kāi)始我們有了泛型.
理論上一切你需要拆裝箱的操作都可以用泛型取代.

4.new對(duì)象操作不一定會(huì)在堆上開(kāi)辟內(nèi)存

在new一個(gè)引用類(lèi)型時(shí),C#會(huì)在堆上為對(duì)象開(kāi)辟一塊內(nèi)存,并返回該內(nèi)存的地址儲(chǔ)存在棧上。 而在new一個(gè)結(jié)構(gòu)體時(shí),編譯器會(huì)識(shí)別到它,并在棧上為其開(kāi)辟內(nèi)存(宇宙第一編譯器的基本操作)。

5.值類(lèi)型不支持多態(tài),適合存儲(chǔ)應(yīng)用程序操作的數(shù)據(jù),而引用則支持多態(tài),適用于定義應(yīng)用程序的行為;

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類(lèi)型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,688評(píng)論 1 32
  • 一:java概述: 1,JDK:Java Development Kit,java的開(kāi)發(fā)和運(yùn)行環(huán)境,java的開(kāi)發(fā)...
    慕容小偉閱讀 1,957評(píng)論 0 10
  • 整理來(lái)自互聯(lián)網(wǎng) 1,JDK:Java Development Kit,java的開(kāi)發(fā)和運(yùn)行環(huán)境,java的開(kāi)發(fā)工具...
    Ncompass閱讀 1,626評(píng)論 0 6
  • 所有知識(shí)點(diǎn)已整理成app app下載地址 J2EE 部分: 1.Switch能否用string做參數(shù)? 在 Jav...
    侯蛋蛋_閱讀 2,716評(píng)論 1 4
  • 寶寶在游樂(lè)場(chǎng)玩累了要吃的,我遞給他一包薯片,“德德的薯片這么多給媽媽分享一片吧,”寶貝把薯片咬了一口,舉手把它放到...
    陽(yáng)光所指閱讀 287評(píng)論 0 1

友情鏈接更多精彩內(nèi)容