第一天:C# 值類型、引用類型、裝箱與拆箱

C#中類型分為兩類:

  • 值類型(Value Type)
  • 引用類型(Reference Type)

值類型和引用類型是以它們?cè)谟?jì)算機(jī)內(nèi)存中是如何被分配的來劃分的。

值類型包括結(jié)構(gòu)和枚舉,值類型又包含一種特殊的值類型,稱為簡(jiǎn)單類型,如:int byte

所有值類型都隱式(在C#代碼中,無法看到繼承關(guān)系,但通過MSIL代碼才可以看到)繼承自System.ValueType,而System.ValueType和引用類型都繼承自System.Object基類

Tips:C#不支持多重繼承,結(jié)構(gòu) 已經(jīng)隱式繼承至System.ValueType,所以 結(jié)構(gòu) 不支持繼承。

什么是堆和棧?

堆和棧的概念:

  • 棧(Stack)是一種后進(jìn)先出的數(shù)據(jù)結(jié)構(gòu),在內(nèi)存中,變量會(huì)被分配在棧上來進(jìn)行操作
  • 堆(heap)是用于為引用類型的實(shí)例(對(duì)象),分配空間的內(nèi)存區(qū)域,在堆上創(chuàng)建一個(gè)對(duì)象,會(huì)將對(duì)象的地址傳給棧上的變量(反過來叫變量指向此對(duì)象,或者變量引用此對(duì)象)-----也就是棧上的變量指向了堆上地址為XXX的實(shí)例(對(duì)象)

值類型

public struct ValPoint{
    public int x;  // 該值類型中的字段
}
ValPoint vPoint1; // ValPoint:結(jié)構(gòu)值類型,vPoint1:變量,此時(shí)并沒有被壓到棧上
vPoint1.x = 10;  // 進(jìn)行入棧操作
Console.WriteLine(vPoint1.x);   //進(jìn)行出棧操作

變量包含了值類型中所有字段,該變量vPoint1 被分配在線程堆棧(Thread Stack)上。

Tips:只有在對(duì)變量操作時(shí),變量才會(huì)進(jìn)行入棧。對(duì)變量的操作,實(shí)際上是一系列的出棧和入棧操作。

結(jié)構(gòu)特點(diǎn)1:

定義的結(jié)構(gòu)內(nèi)所有字段都必須初始化賦值,否則會(huì)報(bào)出“使用了可能為賦值的字段x”.因?yàn)檫@是 .Net 的一個(gè)約束,所有的元素使用前都必須初始化,如:

int i;
Console.WriteLine(i);  // 未初始化變量,使用結(jié)構(gòu)int中內(nèi)部成員在使用前都必須對(duì)它賦值

結(jié)構(gòu)特點(diǎn)2:

調(diào)用結(jié)構(gòu)內(nèi)的方法前,需要對(duì)結(jié)構(gòu)內(nèi)所有字段進(jìn)行賦值

// 修改ValPoint結(jié)構(gòu)
public struct ValPoint{
    public int x;
    public void Block( ){ }
}
//下面代碼將會(huì)編譯錯(cuò)誤
ValPoint vPoint1;
// vPoint1.x = 200;  // 在聲明變量vPoint1后給結(jié)構(gòu)中x變量賦值,才能編譯通過
vPoint1.Block();  //使用了為賦值的局部變量vPoint1
Console.WriteLine(vPoint);    //使用了為賦值的局部變量vPoint1

Tips:如果結(jié)構(gòu)中有多個(gè)字段,則都需要為多個(gè)字段進(jìn)行賦值

Q:上面例子中,結(jié)構(gòu)內(nèi)方法都沒有使用字段x,為什么還要進(jìn)行初始化賦值?這樣子后如果結(jié)構(gòu)中有若干個(gè)字段,初始化賦值豈不麻煩?
A:編譯器隱式地為結(jié)構(gòu)類型創(chuàng)建無參的夠著函數(shù),在這個(gè)構(gòu)造函數(shù)中會(huì)對(duì)結(jié)構(gòu)成員進(jìn)行初始化,所有值類型被賦予0或相當(dāng)于0的值,引用類型被賦予為null值(因此,Struct類型不可自行聲明無參數(shù)的構(gòu)造函數(shù)),所以通過隱式聲明的構(gòu)造函數(shù)去創(chuàng)建一個(gè)ValPoint類型變量:

ValPoint vPoint1 = new ValPoint();
Console.WriteLine(vPoint1.x);   // 輸出為 0

引用類型

聲明一個(gè)引用類型變量,并使用new操作符創(chuàng)建引用類型實(shí)例的時(shí)候,該引用類型的變量會(huì)被分配到線程棧上,該變量只保存了位于堆上的引用類型的實(shí)例的內(nèi)存地址,變量本身不包含任何類型的數(shù)據(jù)。

僅僅定義了變量,沒有使用new操作符的話,由于沒有在堆上創(chuàng)建類型的實(shí)例,因此,該變量值為null,不指向任何對(duì)象(堆上對(duì)象的實(shí)例)

Tips:容易混淆的:變量(Variable)、對(duì)象(Object)、實(shí)例(Instance)。變量可以是值類型,也可以是引用類型,如果是引用類型的話,由于本身只包含對(duì)實(shí)例對(duì)象的引用(內(nèi)存地址),因此也叫對(duì)象引用;而在堆上創(chuàng)建的對(duì)象,稱為對(duì)象的實(shí)例。

public class RefPoint{
    public int x;

    public RefPoint(int x){this.x = x;}
    public RefPoint(){}
}

僅寫下面一條聲明語句時(shí):

RefPoint rPoint1;  // 在線程棧上創(chuàng)建一個(gè)不包含任何數(shù)據(jù),也不指向任何對(duì)象(不包含內(nèi)存地址)的變量

當(dāng)使用new操作符時(shí):

rPoint1 = new RefPoint(1);
  1. 在應(yīng)用程序堆上創(chuàng)建一個(gè)引用類型對(duì)象的實(shí)例,并為它分配內(nèi)存地址
  2. 自動(dòng)傳遞該實(shí)例的引用給構(gòu)造函數(shù)(正因如此,在構(gòu)造函數(shù)中才能使用this來訪問這個(gè)實(shí)例)
  3. 調(diào)用該類型的構(gòu)造函數(shù)
  4. 返回該實(shí)例的引用內(nèi)存地址,復(fù)制給 rPoint1 變量,該rPoint 引用對(duì)象保存的數(shù)據(jù)是指向在堆上創(chuàng)建改類型的實(shí)例地址。

簡(jiǎn)單類型

想比較兩個(gè)int類型是否相等,通常會(huì):

int i = 3;
int j = 3;
if(i == j){ Console.WriteLine("i equals to j");}

但是,對(duì)于自定義的引用類型,如結(jié)構(gòu),就不能使用 “==”來判斷它們是否相等,而需要 Equals() 方法來完成。

string 類型是引用類型,如果:

string a = "123";
string b = "123";
if(a == b){Console.WirteLine("a equals to b");}

其比較的是他們是否指向堆上同一個(gè)對(duì)象。上面顯然不同,但他們所包含的數(shù)值相同,所以,對(duì)string類型用 ‘==’比較,實(shí)際上是比較引用類型內(nèi)包含的值,而不是其引用。

裝箱和拆箱

  • 裝箱: 值類型 -> 等價(jià)的引用類型
int i = 1;
Object boxed = i;
Console.WriteLine("Boxed Point:" + boxed);
  1. 在堆上為新的對(duì)象實(shí)例分配內(nèi)存。該對(duì)象實(shí)例包含數(shù)據(jù),但沒有名稱;
  2. 在棧上值類型變量的值復(fù)制到堆上的對(duì)象中
  3. 將堆上創(chuàng)建的對(duì)象的地址返回給引用類型變量
  • 拆箱:將一個(gè)已裝箱的引用類型 -> 值類型
int i = 5;
Object boxed = i;
j = (int) boxed;
Console.WriteLine("UnBoxed Point : " + j);
  1. 獲取已裝箱的對(duì)象的地址
  2. 將值從堆上的對(duì)象中復(fù)制到堆棧上的值變量中

可見,裝箱與拆箱需要反復(fù)在堆上進(jìn)行操作,所以,在程序中盡量避免無意義的裝箱與拆箱

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評(píng)論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,673評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,610評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評(píng)論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,668評(píng)論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,004評(píng)論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評(píng)論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,173評(píng)論 0 290
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,705評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,426評(píng)論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,656評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評(píng)論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,833評(píng)論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評(píng)論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,371評(píng)論 3 400
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,621評(píng)論 2 380

推薦閱讀更多精彩內(nèi)容

  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,339評(píng)論 11 349
  • 《CLR via C#》作者Jeffrey Richter的話來說,“不理解【引用類型】和【值類型】區(qū)別的程序員將...
    LH_晴閱讀 1,745評(píng)論 0 3
  • 如何才能熱愛生活,對(duì)生活充滿激情?如何才能熱愛生活,對(duì)生活充滿激情? 每當(dāng)自己一個(gè)人待著的時(shí)候我就會(huì)想,活著究竟是...
    小賢哥2017閱讀 3,773評(píng)論 0 0
  • 感恩生命中每一個(gè)貴人對(duì)我的無私幫助,任何機(jī)會(huì)都需要自己去把握去選擇,已經(jīng)選擇就要用心全力以赴的行動(dòng),目標(biāo)就是成功。...
    畢蘭江慈悲閱讀 184評(píng)論 0 0