关于Visual C#装箱与拆箱的研究
生活随笔
收集整理的這篇文章主要介紹了
关于Visual C#装箱与拆箱的研究
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
關(guān)于Visual C#裝箱與拆箱的研究
2004-09-15? 作者: 出處: CSDN
在對(duì)這個(gè)問(wèn)題展開(kāi)討論之前,我們不妨先來(lái)問(wèn)這么幾個(gè)問(wèn)題,以系統(tǒng)的了解我們今天要探究的主題。
觀者也許曾無(wú)數(shù)次的使用過(guò)諸如System.Console類或.NET類庫(kù)中那些品種繁多的類。那么,我想問(wèn)的是它們究竟源自何處?C#又是如何聯(lián)系它們?有沒(méi)有支持我們個(gè)性化擴(kuò)展的機(jī)制或類型系統(tǒng)?又有哪些類型系統(tǒng)可供我們使用呢?如果我們這些PL們連這些問(wèn)題都不知其然,更不知其所以然的話,C#之門恐怕會(huì)把我們拒之門外的。
??????? 那就讓我們先停停手中的活兒,理理頭緒,對(duì)作為.NET重要技術(shù)和基礎(chǔ)之一的CTS(Common Type System)做一個(gè)饒有興趣的研究。顧名思義,CTS就是為了實(shí)現(xiàn)在應(yīng)用程序聲明和使用這些類型時(shí)必須遵循的規(guī)則而存在的通用類型系統(tǒng)。在這要插一句,雖然也許大家都對(duì)此再熟悉不過(guò)了,但是我還是要強(qiáng)調(diào),.Net將整個(gè)系統(tǒng)的類型分成兩大類 —— 值類型 和 引用類型。到此,你也許會(huì)怒斥:說(shuō)了這么半天,你似乎還沒(méi)有切入正題呢!別慌!知道了.Net類型系統(tǒng)的的特點(diǎn)并不代表你真正理解了這個(gè)類型系統(tǒng)的原理和存在的意義。
大多數(shù)面向?qū)ο蟮恼Z(yǔ)言都有兩種類型:原類型(語(yǔ)言固有的類型,如整數(shù)、枚舉)和類。雖然在實(shí)現(xiàn)模塊化和實(shí)體化方面,面向?qū)ο蠹夹g(shù)體現(xiàn)了很強(qiáng)的能力,但是也存在一些問(wèn)題,比如現(xiàn)在提到的這個(gè)系統(tǒng)類型問(wèn)題,歷史告訴我們兩組類型造成了許多問(wèn)題。首先就是兼容性問(wèn)題,這個(gè)也是Microsoft使勁抨擊的一點(diǎn),多數(shù)的OO語(yǔ)言存在這個(gè)弱點(diǎn),原因就是因?yàn)樗麄兊脑愋蜎](méi)有共同的基點(diǎn),于是他們?cè)诒举|(zhì)上并不是真正的對(duì)象,它們并不是從一個(gè)通用基類里派生來(lái)的。怪不得,Anders Heijlsberg 笑稱其為“魔術(shù)類型”。
正是由于這一缺陷,當(dāng)我們希望指定一個(gè)可以接受本語(yǔ)言支持的任何類型的參數(shù)的Method時(shí),同樣的問(wèn)題再次襲擾我們的大腦——不兼容。當(dāng)然,對(duì)于C++的PL大拿,也許這個(gè)沒(méi)有什么大不了的,他們會(huì)自豪的說(shuō),只要用重載的構(gòu)造器為每一種原類型編寫(xiě)一個(gè)Wrapper Class 不就完了嘛!好吧,這樣總算是能共存了,但是,接下來(lái)我們?cè)趺磸倪@個(gè)魔術(shù)中得到我們最關(guān)心的東東 —— 結(jié)果呢?于是,他們依然會(huì)自信的打開(kāi)Boarland,熟練的編寫(xiě)一個(gè)重載過(guò)的函數(shù)來(lái)從剛才的那個(gè) Wrapper Class 中獲取結(jié)果。兄弟 or 姐妹們 ,在當(dāng)時(shí)的歷史條件下,你們的行為是創(chuàng)舉,但是相對(duì)于現(xiàn)在,你將會(huì)為此付出代價(jià) —— 效率低下。畢竟,C++更依賴于對(duì)象,而非面向?qū)ο蟆3姓J(rèn)現(xiàn)實(shí)總比死要面子更理智一些!花這么大力氣,總算把鋪墊說(shuō)完了,我想說(shuō)的是:.Net環(huán)境的CTS 給我們帶來(lái)了方便。第一、CTS中的所有東西都是對(duì)象;第二、所有的對(duì)象都源自一個(gè)基類——System.Object類型。這就是所謂的單根層次結(jié)構(gòu)(singly rooted hierarchy)關(guān)于System.Object的詳細(xì)資料請(qǐng)參考微軟的技術(shù)文檔。這里我們簡(jiǎn)略的談?wù)勆厦嫣岬竭^(guò)的兩大類型:Value Type 和 Reference Type。
CTS值類型的一個(gè)最大的特點(diǎn)是它們不能為null,言外之意就是值類型的變量總有一個(gè)值。在C#中,它包括有原類型、結(jié)構(gòu)、枚舉器。這里需要強(qiáng)調(diào)一點(diǎn):在傳遞值類型的變量時(shí),我們實(shí)際傳遞的是變量的值,而非底層對(duì)象的引用,這一點(diǎn)和傳遞引用類型的變量的情況截然不同;CTS引用類型就好像是類型安全的指針,它可以為null。它包括 如類、接口、委托、數(shù)組等類型。對(duì)比前面值類型的特點(diǎn),當(dāng)我們分配一個(gè)引用類型時(shí),系統(tǒng)會(huì)在后臺(tái)的堆棧上分配一個(gè)值(內(nèi)存分配與位置)并返回對(duì)這個(gè)值的引用;當(dāng)值為null時(shí),說(shuō)明沒(méi)有引用或類型指向某個(gè)對(duì)象。這就意味著,我們?cè)诼暶饕粋€(gè)引用類型的變量時(shí),被操作的是此變量的引用(地址),而不是數(shù)據(jù)。
討論到這個(gè)地方的時(shí)候,本篇的主角終于閃亮登場(chǎng)了——欲吐血或者嘔吐的同志,請(qǐng)?jiān)偃棠鸵幌隆N蚁雴?wèn)一個(gè)問(wèn)題先:在使用這種多類型系統(tǒng)時(shí)如何有效的拓展和提高系統(tǒng)的性能?也許就是在黑板上對(duì)這個(gè)問(wèn)題的探討,西雅圖的那幫家伙們提出了Box(裝箱) and UnBox(拆箱) 的想法。簡(jiǎn)單的說(shuō)。裝箱就是將值類型(value type)轉(zhuǎn)換為引用類型(reference type)的過(guò)程;反之,就是拆箱。(其實(shí)這種思想早八輩子就產(chǎn)生了)。下面我們就進(jìn)一步詳細(xì)的討論裝箱和拆箱的過(guò)程。在討論中,我們剛剛提到的問(wèn)題的答案也就迎刃而解了。
??????? 首先,我們先來(lái)看看裝箱過(guò)程,為此我們需要先做兩個(gè)工作:1、編寫(xiě)例程; 2、打開(kāi)ILDASM(MSIL代碼察看工具)為此我們先來(lái)看看以下的代碼:
代碼中,本篇我們只需要關(guān)注Main()方法下加注釋的兩行代碼,第一行我們創(chuàng)建了一個(gè)double類型的變量(dubBox)。顯然按規(guī)則,CTS規(guī)定double是原類型,所以dubBox自然就是值類型的變量;第二行其實(shí)作了三個(gè)工作,這個(gè)將在下面的MSIL代碼中看的一清二楚。第一步取出dubBox的值,第二步將值類型轉(zhuǎn)換引用類型,第三步傳值給objBox。
MSIL代碼如下:
在MSIL中,第IL_0000 至 IL_0010 行是描述前面兩行代碼的。參照C#的MSIL手冊(cè),觀者不難理解這段底層代碼的執(zhí)行過(guò)程,在這我著重描述一下當(dāng)dubBox被裝箱時(shí)所發(fā)生的故事:(1)劃分堆棧內(nèi)存,在堆棧上分配的內(nèi)存 = dubBox的大小 + objBox及其結(jié)構(gòu)所占用的空間;(2)dubBox的值(77.7699999999996)被復(fù)制到新近分配的堆棧中;(3)將分配給objBox的地址壓棧,此時(shí)它指向一個(gè)object類型,即引用類型。
拆箱作為裝箱的逆過(guò)程,看上去好像很簡(jiǎn)單,其實(shí)里面多了很多值的思考的東西。首先,box的時(shí)候,我們不需要顯式的類型轉(zhuǎn)換,但是在unbox時(shí)就必須進(jìn)行類型轉(zhuǎn)換。這是因?yàn)橐妙愋偷膶?duì)象可以被轉(zhuǎn)換為任何類型。(當(dāng)然,這也是電腦和人腦一個(gè)差別的體現(xiàn))類型轉(zhuǎn)換不容回避的將會(huì)受到來(lái)自CTS管理中心的監(jiān)控——其標(biāo)準(zhǔn)自然是依據(jù)規(guī)則。(其內(nèi)容的容量足以專門設(shè)一章來(lái)討論)好了,我們還是先來(lái)看看下面這段代碼吧:
與前面裝箱的代碼相比,本段代碼多加了一行double dubUnBox = (double)objBox;新加的這行代碼作了四個(gè)工作,這個(gè)也將體現(xiàn)在MSIL代碼中。第一步將一個(gè)值壓入堆棧;第二步將引用類型轉(zhuǎn)換為值類型;第三步間接將值壓棧;第四步傳值給dubUnBox。
MSIL代碼如下:
在MSIL中,第IL_0011 至 IL_0018 行是描述新行代碼的。參照C#的MSIL手冊(cè),觀者不難理解這段底層代碼的執(zhí)行過(guò)程,在此我著重描述一下objBox在拆箱時(shí)的遭遇:(1)環(huán)境須先判斷堆棧上指向合法對(duì)象的地址,以及在對(duì)此對(duì)象向指定的類型進(jìn)行轉(zhuǎn)換時(shí)是否合法,如果不合法,就拋出異常;(2)當(dāng)判斷類型轉(zhuǎn)換正確,就返回一個(gè)指向?qū)ο髢?nèi)的值的指針。
看來(lái),裝箱和拆箱也不過(guò)如此,費(fèi)了半天勁,剛把‘值’給裝到‘箱’里去了,有費(fèi)了更多的勁把它拆解了,郁悶啊!細(xì)心的觀者,可能還能結(jié)合代碼和MSIL看出,怎么在調(diào)用Console.WriteLine()的過(guò)程中又出現(xiàn)了兩次box,是的,我本想偷懶逃過(guò)這節(jié),但是既然已被發(fā)現(xiàn),就應(yīng)該大膽的面對(duì),其實(shí)這就是傳說(shuō)中的“暗箱操作”啊! 因?yàn)镃onsole.WriteLine方法有許多的重載版本,此處的版本是以兩個(gè)String對(duì)象為參數(shù),而具有object 類型的參數(shù)的重載是編譯器找到的最接近的版本,所以,編譯器為了求得與這個(gè)方法的原型一致,就必須對(duì)值類型的dubBox和dubUnBox分別進(jìn)行裝箱(轉(zhuǎn)換成引用類型)。
所以,為了避免由于無(wú)謂的隱式裝箱所造成的性能損失,在執(zhí)行這些多類型重載方法之前,最好先對(duì)值進(jìn)行裝箱。現(xiàn)在我們把上述地代碼改進(jìn)為:
MSIL代碼:
我暈!這算嘛事兒呀!看完后是不是該吐血的吐血,該上吊的上吊呀!相信能堅(jiān)持到看完最后一個(gè) "!" 的同志一定是個(gè)好同志。
其實(shí),我們也可以妄加揣測(cè)一下:引用型應(yīng)當(dāng)屬于高級(jí)類型,而值型屬于原始類型,箱只是一個(gè)概念、一個(gè)秩序、一套規(guī)則或準(zhǔn)確說(shuō)是一個(gè)邏輯。原始的東西作為基礎(chǔ),其復(fù)雜性和邏輯性不會(huì)很高,而高級(jí)的東西就不那么穩(wěn)定了,它會(huì)不斷的進(jìn)化和發(fā)展,因?yàn)檫@個(gè)邏輯的‘箱’會(huì)不斷地被要求擴(kuò)充和完善。由此思路推演,我們就不難預(yù)測(cè)出未來(lái)我們需要努力的方向和成功機(jī)會(huì)可能存在的地方—— !
2004-09-15? 作者: 出處: CSDN
在對(duì)這個(gè)問(wèn)題展開(kāi)討論之前,我們不妨先來(lái)問(wèn)這么幾個(gè)問(wèn)題,以系統(tǒng)的了解我們今天要探究的主題。
觀者也許曾無(wú)數(shù)次的使用過(guò)諸如System.Console類或.NET類庫(kù)中那些品種繁多的類。那么,我想問(wèn)的是它們究竟源自何處?C#又是如何聯(lián)系它們?有沒(méi)有支持我們個(gè)性化擴(kuò)展的機(jī)制或類型系統(tǒng)?又有哪些類型系統(tǒng)可供我們使用呢?如果我們這些PL們連這些問(wèn)題都不知其然,更不知其所以然的話,C#之門恐怕會(huì)把我們拒之門外的。
??????? 那就讓我們先停停手中的活兒,理理頭緒,對(duì)作為.NET重要技術(shù)和基礎(chǔ)之一的CTS(Common Type System)做一個(gè)饒有興趣的研究。顧名思義,CTS就是為了實(shí)現(xiàn)在應(yīng)用程序聲明和使用這些類型時(shí)必須遵循的規(guī)則而存在的通用類型系統(tǒng)。在這要插一句,雖然也許大家都對(duì)此再熟悉不過(guò)了,但是我還是要強(qiáng)調(diào),.Net將整個(gè)系統(tǒng)的類型分成兩大類 —— 值類型 和 引用類型。到此,你也許會(huì)怒斥:說(shuō)了這么半天,你似乎還沒(méi)有切入正題呢!別慌!知道了.Net類型系統(tǒng)的的特點(diǎn)并不代表你真正理解了這個(gè)類型系統(tǒng)的原理和存在的意義。
大多數(shù)面向?qū)ο蟮恼Z(yǔ)言都有兩種類型:原類型(語(yǔ)言固有的類型,如整數(shù)、枚舉)和類。雖然在實(shí)現(xiàn)模塊化和實(shí)體化方面,面向?qū)ο蠹夹g(shù)體現(xiàn)了很強(qiáng)的能力,但是也存在一些問(wèn)題,比如現(xiàn)在提到的這個(gè)系統(tǒng)類型問(wèn)題,歷史告訴我們兩組類型造成了許多問(wèn)題。首先就是兼容性問(wèn)題,這個(gè)也是Microsoft使勁抨擊的一點(diǎn),多數(shù)的OO語(yǔ)言存在這個(gè)弱點(diǎn),原因就是因?yàn)樗麄兊脑愋蜎](méi)有共同的基點(diǎn),于是他們?cè)诒举|(zhì)上并不是真正的對(duì)象,它們并不是從一個(gè)通用基類里派生來(lái)的。怪不得,Anders Heijlsberg 笑稱其為“魔術(shù)類型”。
正是由于這一缺陷,當(dāng)我們希望指定一個(gè)可以接受本語(yǔ)言支持的任何類型的參數(shù)的Method時(shí),同樣的問(wèn)題再次襲擾我們的大腦——不兼容。當(dāng)然,對(duì)于C++的PL大拿,也許這個(gè)沒(méi)有什么大不了的,他們會(huì)自豪的說(shuō),只要用重載的構(gòu)造器為每一種原類型編寫(xiě)一個(gè)Wrapper Class 不就完了嘛!好吧,這樣總算是能共存了,但是,接下來(lái)我們?cè)趺磸倪@個(gè)魔術(shù)中得到我們最關(guān)心的東東 —— 結(jié)果呢?于是,他們依然會(huì)自信的打開(kāi)Boarland,熟練的編寫(xiě)一個(gè)重載過(guò)的函數(shù)來(lái)從剛才的那個(gè) Wrapper Class 中獲取結(jié)果。兄弟 or 姐妹們 ,在當(dāng)時(shí)的歷史條件下,你們的行為是創(chuàng)舉,但是相對(duì)于現(xiàn)在,你將會(huì)為此付出代價(jià) —— 效率低下。畢竟,C++更依賴于對(duì)象,而非面向?qū)ο蟆3姓J(rèn)現(xiàn)實(shí)總比死要面子更理智一些!花這么大力氣,總算把鋪墊說(shuō)完了,我想說(shuō)的是:.Net環(huán)境的CTS 給我們帶來(lái)了方便。第一、CTS中的所有東西都是對(duì)象;第二、所有的對(duì)象都源自一個(gè)基類——System.Object類型。這就是所謂的單根層次結(jié)構(gòu)(singly rooted hierarchy)關(guān)于System.Object的詳細(xì)資料請(qǐng)參考微軟的技術(shù)文檔。這里我們簡(jiǎn)略的談?wù)勆厦嫣岬竭^(guò)的兩大類型:Value Type 和 Reference Type。
CTS值類型的一個(gè)最大的特點(diǎn)是它們不能為null,言外之意就是值類型的變量總有一個(gè)值。在C#中,它包括有原類型、結(jié)構(gòu)、枚舉器。這里需要強(qiáng)調(diào)一點(diǎn):在傳遞值類型的變量時(shí),我們實(shí)際傳遞的是變量的值,而非底層對(duì)象的引用,這一點(diǎn)和傳遞引用類型的變量的情況截然不同;CTS引用類型就好像是類型安全的指針,它可以為null。它包括 如類、接口、委托、數(shù)組等類型。對(duì)比前面值類型的特點(diǎn),當(dāng)我們分配一個(gè)引用類型時(shí),系統(tǒng)會(huì)在后臺(tái)的堆棧上分配一個(gè)值(內(nèi)存分配與位置)并返回對(duì)這個(gè)值的引用;當(dāng)值為null時(shí),說(shuō)明沒(méi)有引用或類型指向某個(gè)對(duì)象。這就意味著,我們?cè)诼暶饕粋€(gè)引用類型的變量時(shí),被操作的是此變量的引用(地址),而不是數(shù)據(jù)。
討論到這個(gè)地方的時(shí)候,本篇的主角終于閃亮登場(chǎng)了——欲吐血或者嘔吐的同志,請(qǐng)?jiān)偃棠鸵幌隆N蚁雴?wèn)一個(gè)問(wèn)題先:在使用這種多類型系統(tǒng)時(shí)如何有效的拓展和提高系統(tǒng)的性能?也許就是在黑板上對(duì)這個(gè)問(wèn)題的探討,西雅圖的那幫家伙們提出了Box(裝箱) and UnBox(拆箱) 的想法。簡(jiǎn)單的說(shuō)。裝箱就是將值類型(value type)轉(zhuǎn)換為引用類型(reference type)的過(guò)程;反之,就是拆箱。(其實(shí)這種思想早八輩子就產(chǎn)生了)。下面我們就進(jìn)一步詳細(xì)的討論裝箱和拆箱的過(guò)程。在討論中,我們剛剛提到的問(wèn)題的答案也就迎刃而解了。
??????? 首先,我們先來(lái)看看裝箱過(guò)程,為此我們需要先做兩個(gè)工作:1、編寫(xiě)例程; 2、打開(kāi)ILDASM(MSIL代碼察看工具)為此我們先來(lái)看看以下的代碼:
| using System; namespace StructApp { /// /// BoxAndUnBox 的摘要說(shuō)明。 /// public class BoxAndUnBox { public BoxAndUnBox() { // // TODO: 在此處添加構(gòu)造函數(shù)邏輯 // } / static void Main(string[] args) { double dubBox = 77.77; /// 定義一個(gè)值形變量 object objBox = dubBox; /// 將變量的值裝箱到 一個(gè)引用型對(duì)象中 Console.WriteLine("The Value is '{0}' and The Boxed is {1}",dubBox,objBox.ToString()); } / } } |
代碼中,本篇我們只需要關(guān)注Main()方法下加注釋的兩行代碼,第一行我們創(chuàng)建了一個(gè)double類型的變量(dubBox)。顯然按規(guī)則,CTS規(guī)定double是原類型,所以dubBox自然就是值類型的變量;第二行其實(shí)作了三個(gè)工作,這個(gè)將在下面的MSIL代碼中看的一清二楚。第一步取出dubBox的值,第二步將值類型轉(zhuǎn)換引用類型,第三步傳值給objBox。
MSIL代碼如下:
| .method private hidebysig static void Main(string[] args) cil managed { .entrypoint // 代碼大小 40 (0x28) .maxstack 3 .locals init ([0] float64 dubBox, [1] object objBox) IL_0000: ldc.r8 77.769999999999996 IL_0009: stloc.0 IL_000a: ldloc.0 IL_000b: box [mscorlib]System.Double IL_0010: stloc.1 IL_0011: ldstr "The Value is '{0}' and The Boxed is {1}" IL_0016: ldloc.0 IL_0017: box [mscorlib]System.Double IL_001c: ldloc.1 IL_001d: callvirt instance string [mscorlib]System.Object::ToString() IL_0022: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_0027: ret } // end of method BoxAndUnBox::Main |
在MSIL中,第IL_0000 至 IL_0010 行是描述前面兩行代碼的。參照C#的MSIL手冊(cè),觀者不難理解這段底層代碼的執(zhí)行過(guò)程,在這我著重描述一下當(dāng)dubBox被裝箱時(shí)所發(fā)生的故事:(1)劃分堆棧內(nèi)存,在堆棧上分配的內(nèi)存 = dubBox的大小 + objBox及其結(jié)構(gòu)所占用的空間;(2)dubBox的值(77.7699999999996)被復(fù)制到新近分配的堆棧中;(3)將分配給objBox的地址壓棧,此時(shí)它指向一個(gè)object類型,即引用類型。
拆箱作為裝箱的逆過(guò)程,看上去好像很簡(jiǎn)單,其實(shí)里面多了很多值的思考的東西。首先,box的時(shí)候,我們不需要顯式的類型轉(zhuǎn)換,但是在unbox時(shí)就必須進(jìn)行類型轉(zhuǎn)換。這是因?yàn)橐妙愋偷膶?duì)象可以被轉(zhuǎn)換為任何類型。(當(dāng)然,這也是電腦和人腦一個(gè)差別的體現(xiàn))類型轉(zhuǎn)換不容回避的將會(huì)受到來(lái)自CTS管理中心的監(jiān)控——其標(biāo)準(zhǔn)自然是依據(jù)規(guī)則。(其內(nèi)容的容量足以專門設(shè)一章來(lái)討論)好了,我們還是先來(lái)看看下面這段代碼吧:
| using System; namespace StructApp { /// /// BoxAndUnBox 的摘要說(shuō)明。 /// public class BoxAndUnBox { public BoxAndUnBox() { // // TODO: 在此處添加構(gòu)造函數(shù)邏輯 // } / static void Main(string[] args) { double dubBox = 77.77; object objBox = dubBox; double dubUnBox = (double)objBox; /// 將引用型對(duì)象拆箱 ,并返回值 Console.WriteLine("The Value is '{0}' and The UnBoxed is {1}",dubBox,dubUnBox); } / } } |
與前面裝箱的代碼相比,本段代碼多加了一行double dubUnBox = (double)objBox;新加的這行代碼作了四個(gè)工作,這個(gè)也將體現(xiàn)在MSIL代碼中。第一步將一個(gè)值壓入堆棧;第二步將引用類型轉(zhuǎn)換為值類型;第三步間接將值壓棧;第四步傳值給dubUnBox。
MSIL代碼如下:
| .method private hidebysig static void Main(string[] args) cil managed { .entrypoint // 代碼大小 48 (0x30) .maxstack 3 .locals init ([0] float64 dubBox, [1] object objBox, [2] float64 dubUnBox) IL_0000: ldc.r8 77.769999999999996 IL_0009: stloc.0 IL_000a: ldloc.0 IL_000b: box [mscorlib]System.Double IL_0010: stloc.1 IL_0011: ldloc.1 IL_0012: unbox [mscorlib]System.Double IL_0017: ldind.r8 IL_0018: stloc.2 IL_0019: ldstr "The Value is '{0}' and The UnBoxed is {1}" IL_001e: ldloc.0 IL_001f: box [mscorlib]System.Double IL_0024: ldloc.2 IL_0025: box [mscorlib]System.Double IL_002a: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_002f: ret } // end of method BoxAndUnBox::Main |
在MSIL中,第IL_0011 至 IL_0018 行是描述新行代碼的。參照C#的MSIL手冊(cè),觀者不難理解這段底層代碼的執(zhí)行過(guò)程,在此我著重描述一下objBox在拆箱時(shí)的遭遇:(1)環(huán)境須先判斷堆棧上指向合法對(duì)象的地址,以及在對(duì)此對(duì)象向指定的類型進(jìn)行轉(zhuǎn)換時(shí)是否合法,如果不合法,就拋出異常;(2)當(dāng)判斷類型轉(zhuǎn)換正確,就返回一個(gè)指向?qū)ο髢?nèi)的值的指針。
看來(lái),裝箱和拆箱也不過(guò)如此,費(fèi)了半天勁,剛把‘值’給裝到‘箱’里去了,有費(fèi)了更多的勁把它拆解了,郁悶啊!細(xì)心的觀者,可能還能結(jié)合代碼和MSIL看出,怎么在調(diào)用Console.WriteLine()的過(guò)程中又出現(xiàn)了兩次box,是的,我本想偷懶逃過(guò)這節(jié),但是既然已被發(fā)現(xiàn),就應(yīng)該大膽的面對(duì),其實(shí)這就是傳說(shuō)中的“暗箱操作”啊! 因?yàn)镃onsole.WriteLine方法有許多的重載版本,此處的版本是以兩個(gè)String對(duì)象為參數(shù),而具有object 類型的參數(shù)的重載是編譯器找到的最接近的版本,所以,編譯器為了求得與這個(gè)方法的原型一致,就必須對(duì)值類型的dubBox和dubUnBox分別進(jìn)行裝箱(轉(zhuǎn)換成引用類型)。
所以,為了避免由于無(wú)謂的隱式裝箱所造成的性能損失,在執(zhí)行這些多類型重載方法之前,最好先對(duì)值進(jìn)行裝箱。現(xiàn)在我們把上述地代碼改進(jìn)為:
| using System; namespace StructApp { /// /// BoxAndUnBox 的摘要說(shuō)明。 /// public class BoxAndUnBox { public BoxAndUnBox() { // // TODO: 在此處添加構(gòu)造函數(shù)邏輯 // } /// static void Main(string[] args) { double dubBox = 77.77; object objBox = dubBox; double dubUnBox = (double)objBox; object objUnBox = dubUnBox; Console.WriteLine("The Value is '{0}' and The UnBoxed is {1}",objBox,objUnBox); } /// } } |
MSIL代碼:
| .method private hidebysig static void Main(string[] args) cil managed { .entrypoint // 代碼大小 45 (0x2d) .maxstack 3 .locals init ([0] float64 dubBox, [1] object objBox, [2] float64 dubUnBox, [3] object objUnBox) IL_0000: ldc.r8 77.769999999999996 IL_0009: stloc.0 IL_000a: ldloc.0 IL_000b: box [mscorlib]System.Double IL_0010: stloc.1 IL_0011: ldloc.1 IL_0012: unbox [mscorlib]System.Double IL_0017: ldind.r8 IL_0018: stloc.2 IL_0019: ldloc.2 IL_001a: box [mscorlib]System.Double IL_001f: stloc.3 IL_0020: ldstr "The Value is '{0}' and The UnBoxed is {1}" IL_0025: ldloc.1 IL_0026: ldloc.3 IL_0027: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_002c: ret } // end of method BoxAndUnBox::Main |
我暈!這算嘛事兒呀!看完后是不是該吐血的吐血,該上吊的上吊呀!相信能堅(jiān)持到看完最后一個(gè) "!" 的同志一定是個(gè)好同志。
其實(shí),我們也可以妄加揣測(cè)一下:引用型應(yīng)當(dāng)屬于高級(jí)類型,而值型屬于原始類型,箱只是一個(gè)概念、一個(gè)秩序、一套規(guī)則或準(zhǔn)確說(shuō)是一個(gè)邏輯。原始的東西作為基礎(chǔ),其復(fù)雜性和邏輯性不會(huì)很高,而高級(jí)的東西就不那么穩(wěn)定了,它會(huì)不斷的進(jìn)化和發(fā)展,因?yàn)檫@個(gè)邏輯的‘箱’會(huì)不斷地被要求擴(kuò)充和完善。由此思路推演,我們就不難預(yù)測(cè)出未來(lái)我們需要努力的方向和成功機(jī)會(huì)可能存在的地方—— !
總結(jié)
以上是生活随笔為你收集整理的关于Visual C#装箱与拆箱的研究的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: XML的二十个热点问题
- 下一篇: 警惕!新版Net Transport(影