C#中的值類型和引用類型

一.C#中的值類型和引用類型

  • 概念

值類型直接存儲其值。

引用類型存儲對值的引用骚烧。

說起來有些拗口浸赫,其本質(zhì)是ValueReference的區(qū)別闰围,在文檔翻譯過程中也有譯者將Reference翻譯為參考。兩種類型在內(nèi)存中的存儲方式有顯著區(qū)別既峡。

  • 不同的存儲對象

值類型變量存儲的是變量的值羡榴,直接儲存在棧內(nèi)存中。

引用類型變量存儲的是變量所在的內(nèi)存地址运敢,引用類型變量的實際數(shù)據(jù)存儲于托管堆校仑,變量本身僅僅是一個指向堆中實際數(shù)據(jù)的地址,存儲于棧內(nèi)存中传惠,通常是四個字節(jié)迄沫。

  • 不同的存儲位置

值類型Value存儲在線程堆棧中

引用類型Reference存儲在托管堆上

內(nèi)存格局通常劃分為四個區(qū):

全局數(shù)據(jù)區(qū):存放全局變量,靜態(tài)數(shù)據(jù)涉枫,常量

代碼區(qū):存放所有的程序代碼

棧區(qū):存放為運行而分配的局部變量邢滑,參數(shù),返回數(shù)據(jù)愿汰,返回地址等

堆區(qū):即自由存儲區(qū)

為了理解值類型變量和引用類型變量的內(nèi)存分配模型困后,我們應先區(qū)分兩種不同的內(nèi)存區(qū)域——線程堆棧Thread Stack和托管堆Managed Heap

每一個正在運行的程序都對應著一個進程Process衬廷,在一個進程內(nèi)部摇予,可以有一個或多個線程Thread,每個線程都擁有一塊“自留地”吗跋,成為線程堆棧,大小為1M侧戴,用于保存自身的一些數(shù)據(jù),如函數(shù)中定義的局部變量跌宛、函數(shù)調(diào)用時傳送的參數(shù)值等酗宋。

現(xiàn)在我們可以解釋第一句話——值類型存儲在線程堆棧中,也就是說所有值類型的變量都是在線程堆棧中分配的疆拘。

另一塊內(nèi)存區(qū)域稱為堆Heap,在.NET這種托管環(huán)境下蜕猫,堆由CLR(Common Language Runtime)管理,所以又稱托管堆Managed Heap哎迄。例如使用new關鍵字創(chuàng)建類的對象實例時回右,分配給對象的內(nèi)存單元就位于托管堆中。

  • 不同的類型

這里類型區(qū)分的對象是C#中內(nèi)建的類型Type和用戶自定義的類型漱挚。

C#中的值類型:C#有15個預定義類型,其中13個是值類型,兩個是引用類型(stringobject)翔烁。

C#中的值類型和引用類型

由此分類可以得知,struct是輕量級的類這句話本質(zhì)上就不成立旨涝,兩者的內(nèi)存模型和行為表現(xiàn)都有區(qū)別蹬屹。

  • 不同的表現(xiàn)

1.值類型的表現(xiàn)

int a = 5;
int b = a;

上面這段代碼中我們賦予a一個常量值5,而賦予ba的值,這會在內(nèi)存中兩個不同的地方存儲值20哩治。我們改變a的值秃踩,不會影響b的值,這兩個值時獨立存儲的业筏。可以在上述代碼之后改變a的值鸟赫,輸出b的值進行查看蒜胖。

2.引用類型的表現(xiàn)

首先創(chuàng)建一個簡單的類,只包含一個int類型的屬性抛蚤。

    class TestRef
    {
        public int A { get; set; }
    }

主方法中與值類型的代碼類型:

        public static void Main(string[] args)
        {
            TestRef testA = new TestRef {A = 20};
            TestRef testB = testA;  // 將testA賦值給testB

            Console.WriteLine("Before:testA中A的值:{0}", testA.A);
            Console.WriteLine("Before:testB中A的值:{0}", testB.A);

            testB.A = 15;   // 改變testB的屬性值

            Console.WriteLine("After:testA中A的值:{0}",testA.A);
            Console.WriteLine("After:testB中A的值:{0}", testB.A);
            Console.ReadKey();
        }

運行結果如圖所示:


運行結果

可以看到testB改變了屬性值之后台谢,testA的屬性值也隨之改變,這是由于這兩個對象只是一個指向堆內(nèi)存的地址岁经,實際指向的只有一份實際的值朋沮。

3.與null的關系
如果變量是引用類型變量,則可以將其值設置為null缀壤,表示它不引用任何對象(可以將理解為將指針指向空)樊拓。而值類型不能為null,這也是為什么值類型初始化時必須指定初始值或默認值塘慕。

  • 設計立足點

    大多數(shù)更復雜的數(shù)據(jù)類型,包括我們自己聲明的類都是引用類型筋夏。它們分配在堆中,其生存期可以跨多個函數(shù)調(diào)用,可以通過一個或幾個別名來訪問。CLR執(zhí)行一種精細的算法,來跟蹤哪些引用變量仍是可以訪問的,哪些引用變量已經(jīng)不能訪問了图呢。CLR會定期刪除不能訪問的對象,把它們占用的內(nèi)存返回給操作系統(tǒng)条篷。這是通過垃圾收集器實現(xiàn)的。

    把基本類型規(guī)定為值類型,而把包含許多字段的較大類型(通常在有類的情況下)規(guī)定為引用類型,C#設計這種方式的原因是可以得到最佳性能蛤织。如果要把自己的類型定義為值類型,就應把它聲明為一個結構赴叹。

  • 深拷貝和淺拷貝

深拷貝——源對象與拷貝對象互相獨立,其中任何一個對象的改動都不會對另外一個對象造成影響指蚜。

淺拷貝——拷貝對象后乞巧,兩個對象并未完全“分離”,改變一個對象實際儲存的內(nèi)容姚炕,則兩個對象同時被改變摊欠。

這種差異的產(chǎn)生,即是取決于拷貝子對象時復制內(nèi)存還是復制指針柱宦。深拷貝為子對象重新分配了一段內(nèi)存空間些椒,并復制其中的內(nèi)容;淺拷貝僅僅將指針指向原來的子對象掸刊。

我們假設有了一個對象orignalObj免糕,并且對象orignalObj已經(jīng)有了一些具體的值,現(xiàn)在我們想創(chuàng)建一個orignalObj的副本即對象copyObj,我們希望牌芋,操作對象copyObj的同時不改變對象orignalObj的值,也就是說對象a和對象b是兩個完全獨立的對象躺屁,這即是深拷貝经宏。

當兩個對象指向同一個地址時犀暑,如果我們改變其中一個對象的值,另一個對象也被相應的改變烁兰,這即是淺拷貝耐亏。

  • 額外需要注意

(1)String字符串對象是引用對象沪斟,但是很特殊,它表現(xiàn)的如值對象一樣主之,即對它進行賦值择吊,分割,合并杀餐,并不是對原有的字符串進行操作干发,而是返回一個新的字符串對象史翘。但這其實是運算符重載的結果,將string實現(xiàn)為語義遵循一般的琼讽、直觀的字符串規(guī)則。 String對象被分配在堆上,而不是棧上钻蹬。

(2)Array數(shù)組對象是引用對象,在進行賦值的時候肝匆,實際上返回的是源對象的另一份引用而已;因此如果要對數(shù)組對象進行真正的復制(深拷貝)旗国,那么需要新建一份數(shù)組對象注整,然后將源數(shù)組的值逐一拷貝到目的對象中能曾。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末寿冕,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子驼唱,更是在濱河造成了極大的恐慌,老刑警劉巖玫恳,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纽窟,死亡現(xiàn)場離奇詭異,居然都是意外死亡臂港,警方通過查閱死者的電腦和手機视搏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進店門审孽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來浑娜,“玉大人,你說我怎么就攤上這事打颤±焯希” “怎么了编饺?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵响驴,是天一觀的道長。 經(jīng)常有香客問我豁鲤,道長,這世上最難降的妖魔是什么琳骡? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任日熬,我火速辦了婚禮肾胯,結果婚禮上耘纱,老公的妹妹穿的比我還像新娘敬肚。我一直安慰自己束析,他們只是感情好,可當我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布弄慰。 她就那樣靜靜地躺著蝶锋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪扳缕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天驴剔,我揣著相機與錄音粥庄,去河邊找鬼。 笑死惜互,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的炒事。 我是一名探鬼主播,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼挠乳,長吁一口氣:“原來是場噩夢啊……” “哼姑躲!你這毒婦竟也來了?” 一聲冷哼從身側響起黍析,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤阐枣,失蹤者是張志新(化名)和其女友劉穎奄抽,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逞度,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡妙啃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年揖赴,在試婚紗的時候發(fā)現(xiàn)自己被綠了馆匿。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片燥滑。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖腔稀,靈堂內(nèi)的尸體忽然破棺而出羽历,到底是詐尸還是另有隱情淡喜,我是刑警寧澤秕磷,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布澎嚣,位于F島的核電站,受9級特大地震影響易桃,放射性物質(zhì)發(fā)生泄漏锌俱。R本人自食惡果不足惜晤郑,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一造寝、第九天 我趴在偏房一處隱蔽的房頂上張望吭练。 院中可真熱鬧诫龙,春花似錦鲫咽、人聲如沸谷异。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽括丁。三九已至,卻和暖如春尖昏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背抽诉。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工吐绵, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人己单。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓纹笼,卻偏偏與公主長得像纹份,于是被迫代替她去往敵國和親廷痘。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,500評論 2 359

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

  • 從三月份找實習到現(xiàn)在元暴,面了一些公司兄猩,掛了不少,但最終還是拿到小米枢冤、百度、阿里掏导、京東、新浪趟咆、CVTE梅屉、樂視家的研發(fā)崗...
    時芥藍閱讀 42,274評論 11 349
  • Java8張圖 11鳞贷、字符串不變性 12、equals()方法惰聂、hashCode()方法的區(qū)別 13、...
    Miley_MOJIE閱讀 3,709評論 0 11
  • __block和__weak修飾符的區(qū)別其實是挺明顯的:1.__block不管是ARC還是MRC模式下都可以使用搓幌,...
    LZM輪回閱讀 3,329評論 0 6
  • 原來這個時候肯定早睡了饲趋,因為我有催眠神器——VOA,今天晚上沒有聽堂污,一直在想學英語的事。能聽到窗外的雨聲敷鸦,像背景音...
    愛如捕風閱讀 1,153評論 1 24
  • 1寝贡、工字搓背(100次) 感冒咳嗽的預防和治療值依、腎臟疾病圃泡、可增強體質(zhì)愿险,促進生長 2、搓 鼻翼 加速血液循環(huán)辆亏、預防...
    楊觀仙閱讀 417評論 0 1