浅谈.NET中的类型和装箱、拆箱原理
生活随笔
收集整理的這篇文章主要介紹了
浅谈.NET中的类型和装箱、拆箱原理
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
談到裝箱拆箱,大概的意思就是值類型和引用類型的相互轉(zhuǎn)換唄---值類型到引用類型叫裝箱,反之則叫拆箱。這當(dāng)然沒有問題,可是你只知道這么多,那么建議你花點時間看看樓主這篇文章 1. .NET中的類型 為了說明裝箱和拆箱,那首先必須先說類型。在.NET中,我們知道System.Object類型是所有內(nèi)建類型的基類。注意這里說的是內(nèi)建類型,程序員可以編寫不繼承子自System.Object的類型,這里不做過多的介紹(感興趣的博友可以研究一下)。 所有.NET的類型都可以分為兩類(有點不嚴(yán)謹(jǐn),但是大家都這么講):值類型和引用類型。那么值類型和引用類型如何區(qū)分,標(biāo)準(zhǔn)是什么?最簡單也最明確的一個區(qū)分標(biāo)準(zhǔn)是:所有的值類型都繼承自System.ValueType(System.ValueType繼承自System.Object),也就是說,所有繼承自System.ValueType的類型都是值類型,而其他類型都是引用類型。(題外話:以前在讀一位博友王濤的《你必須知道的.NET》中,他說,值類型和引用類型最本質(zhì)的區(qū)別是:值類型和引用類型在內(nèi)存中分配的位置不同,前者分配在堆棧上,后者分配在堆上。個人覺得這個不是一個簡單明確的區(qū)分方法。) 說到這里,你應(yīng)該要有這樣的想法:嚴(yán)格來說的話,System.Object作為所有內(nèi)建類型的基類,本身并沒有值類型和引用類型之分。但是System.Object的對象,具有引用類型的特點。這也是值類型在有些場合需要裝箱拆箱的原因。 下面還是簡單說下值類型和引用類型的不一樣的地方吧,分3塊,個人覺得理解這3塊就可以了: 變量賦值?? 值類型的變量將直接獲得一個真實的數(shù)據(jù)副本,而對引用類型的賦值僅僅是吧對象的引用賦給變量,這樣就可能導(dǎo)致多個變量引用到一個實際對象實例上(這里需要各位博友去理解.NET對String的一些優(yōu)化機制,本質(zhì)和這個不相悖)。 內(nèi)存分配?? 引用類型的對象將在堆上分配內(nèi)存,而值類型的對象則會在堆棧上分配內(nèi)存。(內(nèi)存如何分配:堆棧上存的是什么?值類型變量和引用類型變量的引用。堆上存的是什么?引用類型的對象(包括了類型對象指針和同步塊索引,注意只是個索引,這是.NET為線程同步提出的一種折中的辦法。))。大對象堆(也是堆,一種特別的堆)什么的這里不做介紹。但必須說明的是:堆棧的空間有限,但運行效率卻比堆要高得多!!! 由于所有的值類型都繼承自System.ValueType,而System.ValueType繼承自System.Object,并重新實現(xiàn)了基類System.Object的一個虛方法Equals,而引用類型并沒有重寫。 2.裝箱拆箱原理 前面簡單介紹了.NET中的類型,下面引入裝箱和拆箱。通過1我們知道值類型的對象是在堆棧上分配內(nèi)存的,而引用類型(包括System.Object)對象是在堆上分配內(nèi)存的,那么當(dāng)值類型被類型轉(zhuǎn)換時,會在堆棧和堆上進(jìn)行一系列的操作,這就是裝箱拆箱的來源。 充分理解裝箱拆箱的原理,有助于我們程序員寫出高效的代碼。 梳理下:前面DebugLZQ說到,所有值類型都繼承自System.ValueType,而Sytem.ValueType繼承自System.Object;所有值類型對象都分配在堆棧上,而所有引用類型,當(dāng)然包括System.Object,對象都分配在堆上。那么,問題來了:既然System.Object 是所有值類型的基類,那么所有值類型必然可以隱式轉(zhuǎn)換成System.Object(面向?qū)ο笾械念愋吞鎿Q原則,基類能夠替換子類),那么這個對象將被分配在哪里,堆上還是堆棧上?事實上,當(dāng)這個轉(zhuǎn)換發(fā)生時,CLR需要做額外的工作把堆棧上的值類型移動到堆上,這個操作就是被我們稱作的“裝箱”。 裝箱(box)的詳細(xì)步驟: 在堆上分配一個內(nèi)存空間,大小等于需要裝箱的值類型對象的大小加上兩個引用類型對象都擁有的成員:類型對象指針和同步塊引用。 把堆棧上的值類型對象復(fù)制到堆上新分配的對象。 返回一個指向堆上新對象的引用,并且存儲到堆棧上被裝箱的那個值類型的對象里。 這個步驟不需要程序員自己編寫,在任何出現(xiàn)裝箱的地方,編譯器會自動加上執(zhí)行以上功能的IL代碼。 所謂的拆箱就是裝箱對應(yīng)著的概念,但拆箱的過程和裝箱并不是倒過來就是: 拆箱(unbox.any)的詳細(xì)步驟 如果為待拆箱對象為null,拋出NullReferenceException異常。 如果引用指向的不是一個期望對象的已裝箱對象,拋出InvalidCastException異常。 獲取已裝箱對象中各個字段的地址,這個過程就是“拆箱” 需要說明的是一般拆箱以后會伴隨著對象的拷貝,但拷貝操作已經(jīng)不是拆箱的范疇。 裝箱拆箱新能比較 了解了裝箱和拆箱的操作,我們可以清楚的明白:裝箱操作會導(dǎo)致數(shù)據(jù)在堆和棧上進(jìn)行拷貝,頻繁的裝箱操作會性能損失。而相比而言拆箱過程對性能損耗還是比較小的。 3 小結(jié) 裝箱和拆箱意味著堆和堆棧空間的一系列操作,毫無疑問,這些操作的性能代價是很大的,尤其是對于堆上空間的操作,速度相對于堆棧慢得多,并且可能引發(fā)垃圾回收,這些都將大規(guī)模的影響系統(tǒng)系能。 裝箱和拆箱操作經(jīng)常發(fā)生在以下連個場合:
- 值類型的格式化輸出
- System.Object類型的容器
?
第二種情況更為常見一些,例如常用的容器ArrayList,就是一個典型的System.Object容器,任何值類型被放入到ArrayList的對象中,都會發(fā)生一次裝箱操作,而對應(yīng)的取出值類型對象會引發(fā)一次拆箱操作。 在.NET 2.0以后,引入了“泛型”的概念后,這些問題得到了有效的解決。泛型允許定義針對某個特定類型(包括值類型)的容器,并且避免裝箱和拆箱。轉(zhuǎn)載于:https://www.cnblogs.com/minily/p/7390580.html
總結(jié)
以上是生活随笔為你收集整理的浅谈.NET中的类型和装箱、拆箱原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android 百度鹰眼轨迹SDK(v2
- 下一篇: spring集成kafka