裝箱、轉(zhuǎn)型、方法調(diào)用他們究竟有什么區(qū)別?_.Net教程
推薦:使用Ajax后,原來導(dǎo)出功能失敗的解決方法問題描述:我們的產(chǎn)品在Ajax后(使用微軟的UpdatePanel),其中的導(dǎo)出功能出現(xiàn)錯(cuò)誤。因?yàn)閷?dǎo)出功能使用了Response直接輸出內(nèi)容,而Ajax的異步方式對(duì)此不能解析導(dǎo)致出現(xiàn)錯(cuò)誤。 解決過程:在網(wǎng)上
| 以下為引用的內(nèi)容: 裝箱、轉(zhuǎn)型、方法調(diào)用這些我們天天進(jìn)行的日常工作之前到底有什么差別? |
| 以下為引用的內(nèi)容: struct UserInfoStruct 2{ 3 public int UserId; 4 public string UserName; 5} 6class UserInfoClass 7{ 8 private int UserId; 9 private string UserName; 10} 11class Program 12{ 13 14 static void Main(string[] args) 15 { 16 object objString = "abc"; 17 18 string aString = (string)objString; 19 string bString = objString.ToString(); 20 string cString = Convert.ToString(objString); 21 22 object objInt = 5; 23 int aInt = (int)objInt; 24 int bInt = Convert.ToInt32(objInt); 25 26 object objStruct = new UserInfoStruct(); 27 UserInfoStruct aUserInfoStruct = (UserInfoStruct)objStruct; 28 29 object objClass = new UserInfoClass(); 30 UserInfoClass aUserInfoClass = (UserInfoClass)objClass; 31 } 32} |
前幾天在群里聊天,有人問:
string aString = (string)objString;
string bString = objString.ToString();有什么區(qū)別,我當(dāng)時(shí)就回答“一個(gè)是轉(zhuǎn)型、一個(gè)是方法調(diào)用”,剛說完就覺得自己是在說廢話,其實(shí)我也不知道內(nèi)部到底發(fā)生了什么,如是就reflector,ILDASM,google一起上,現(xiàn)在把弄出來的結(jié)果整理了一下,share出來,并把相似的幾個(gè)都集在一起討論,由于我不懂WinDbg,所以無法深入,就淺嘗輒止吧。
下面是main方法的IL代碼:
| 以下為引用的內(nèi)容: 1.method private hidebysig static void Main(string[] args) cil managed 2{ 3 .entrypoint 4 // Code size 97 (0x61) 5 .maxstack 1 6 .locals init ([0] object objString, 7 [1] string aString, 8 [2] string bString, 9 [3] string cString, 10 [4] object objInt, 11 [5] int32 aInt, 12 [6] int32 bInt, 13 [7] object objStruct, 14 [8] valuetype SomeKits.UserInfoStruct aUserInfoStruct, 15 [9] object objClass, 16 [10] class SomeKits.UserInfoClass aUserInfoClass, 17 [11] valuetype SomeKits.UserInfoStruct CS$0$0000) 18 IL_0000: nop 19 IL_0001: ldstr "abc" 20 IL_0006: stloc.0 21 IL_0007: ldloc.0 22 IL_0008: castclass [mscorlib]System.String 23 IL_000d: stloc.1 24 IL_000e: ldloc.0 25 IL_000f: callvirt instance string [mscorlib]System.Object::ToString() 26 IL_0014: stloc.2 27 IL_0015: ldloc.0 28 IL_0016: call string [mscorlib]System.Convert::ToString(object) 29 IL_001b: stloc.3 30 IL_001c: ldc.i4.5 31 IL_001d: box [mscorlib]System.Int32 32 IL_0022: stloc.s objInt 33 IL_0024: ldloc.s objInt 34 IL_0026: unbox.any [mscorlib]System.Int32 35 IL_002b: stloc.s aInt 36 IL_002d: ldloc.s objInt 37 IL_002f: call int32 [mscorlib]System.Convert::ToInt32(object) 38 IL_0034: stloc.s bInt 39 IL_0036: ldloca.s CS$0$0000 40 IL_0038: initobj SomeKits.UserInfoStruct 41 IL_003e: ldloc.s CS$0$0000 42 IL_0040: box SomeKits.UserInfoStruct 43 IL_0045: stloc.s objStruct 44 IL_0047: ldloc.s objStruct 45 IL_0049: unbox.any SomeKits.UserInfoStruct 46 IL_004e: stloc.s aUserInfoStruct 47 IL_0050: newobj instance void SomeKits.UserInfoClass::.ctor() 48 IL_0055: stloc.s objClass 49 IL_0057: ldloc.s objClass 50 IL_0059: castclass SomeKits.UserInfoClass 51 IL_005e: stloc.s aUserInfoClass 52 IL_0060: ret 53} |
將IL代碼和源代碼比較得知
string aString = (string)objString;的IL代碼是 castclass [mscorlib]System.String
這個(gè)過程發(fā)生了什么?首先在這個(gè)指令之前l(fā)dloc.0是將第一個(gè)局部變量的引用壓入堆棧中,然后從堆棧頂上彈出對(duì)象的引用,將這個(gè)引用轉(zhuǎn)型為這個(gè)指令指定的類型,如果轉(zhuǎn)型成功的話將轉(zhuǎn)型的結(jié)果壓入棧頂。那什么情況下轉(zhuǎn)型成功,什么情況下轉(zhuǎn)型將不成功呢?當(dāng)這個(gè)棧頂?shù)膶?duì)象不是期望的類的子類的話那就轉(zhuǎn)型失敗了,就會(huì)拋出InvalidCastException異常。那如果棧頂?shù)膶?duì)象是null怎么辦?會(huì)觸發(fā)異常么?答案是不會(huì),如果棧頂上的元素是null的時(shí)候,轉(zhuǎn)型結(jié)果也是null,不會(huì)引發(fā)什么異常。
對(duì)于string bString = objString.ToString()就沒有什么好說的了,從生成的代碼callvirt instance string [mscorlib]System.Object::ToString()來看,它調(diào)用了object的ToString()方法,使用的是callvirt指令,那實(shí)際上調(diào)用的是string類里面重寫object的那個(gè)ToString()。
string cString = Convert.ToString(objString)這種形式在內(nèi)部到底發(fā)生了什么呢?我們看看Convert類的ToString(object)靜態(tài)方法的實(shí)現(xiàn):
| 以下為引用的內(nèi)容: public static string ToString(object value) { return ToString(value, null); } public static string ToString(object value, IFormatProvider provider) |
在Convert.ToString()方法里,首先將對(duì)象嘗試轉(zhuǎn)型為IConvertible接口,如果轉(zhuǎn)型成功就會(huì)調(diào)用這個(gè)接口的ToString()方法了,所以你想想,如果我們要讓我們自己寫的類型支持Convert.ToString()這種寫法怎么辦?那就實(shí)現(xiàn)IConvertible接口吧。
object objInt = 5這個(gè)又發(fā)生了什么?它對(duì)應(yīng)的IL指令是:box [mscorlib]System.Int32,box是裝箱指令,具體分三步進(jìn)行:
1.在托管堆上分配一塊內(nèi)存,內(nèi)存的大小是值類型的大小然后加上兩個(gè)所有引用類型都有的附加字段:SyncBlockIndex和一個(gè)放發(fā)表指針
2.將棧上的值類型拷貝到剛才申請(qǐng)的類型中
3.返回剛在托管堆上申請(qǐng)的對(duì)象引用,將其壓入棧
從這里看裝箱不僅僅耗費(fèi)內(nèi)存還將東西拷貝來拷貝去的,真是賠了夫人又折兵啊。
int aInt = (int)objInt又干了些什么呢?還是類型轉(zhuǎn)換么?它對(duì)應(yīng)的IL代碼是
unbox.any [mscorlib]System.Int32
這個(gè)稱之為拆箱,顧名思義就是將剛才的已裝箱類型給“轉(zhuǎn)換”為未裝箱時(shí)候的值類型,從這個(gè)層面看拆箱好像是裝箱的“逆過程”,實(shí)際上卻不是,拆箱是通過這樣的兩步進(jìn)行的:
1.從棧上獲取托管堆中已裝箱對(duì)象的地址
2.從已裝箱對(duì)象中獲取剛才那個(gè)拷貝過去的值類型的地址
看到?jīng)],拆箱比起裝箱起來少了一步,這里并沒有將已裝箱類型中的值類型拷貝到棧上,看起來拆箱并沒有涉及到內(nèi)存的拷貝操作,它做的僅僅是做一下地址的提取,但是實(shí)際中拆箱后往往緊跟著的就是內(nèi)存的拷貝。從上面的代碼中我們可以看到裝箱和拆箱是很消耗資源的操作,所以我們需要特別注意,特別是一些隱式的,我們常常忽略了。
按照上一小節(jié)的結(jié)論,string cString = Convert.ToString(objString)能夠編譯通過是因?yàn)閕nt類型實(shí)現(xiàn)了IConvertible接口,通過Reflector查看代碼果真如此。
上面是對(duì).net基元類型的一些討論,那么對(duì)于自己寫的struct和class是怎樣的呢?
通過IL代碼,可知對(duì)于值類型的struct
object objStruct = new UserInfoStruct();
UserInfoStruct aUserInfoStruct = (UserInfoStruct)objStruct;
就是裝箱拆箱的過程
對(duì)于引用類型的class UserInfoClass aUserInfoClass = (UserInfoClass)objClass就是castclass指令的操作。
由于本人對(duì)WinDbg一無所知,所以也無法在更深一層次討論這些機(jī)制的最底層實(shí)現(xiàn),實(shí)屬遺憾,希望能有一些達(dá)人對(duì)底層做進(jìn)一步解釋。
分享:ASP.NET蔚昜璃唗蹈趙傖Binary揣湔祫DB or File郔輪衄?婓枒蹦涴跺恀枙..苤萊竭屾?zhèn)`涴跺陲昹..憩善厙繚奻梑賸珨虳訧埭..諒湮模?睡蔚昜璃唗蹈趙傖Binary Data..?綴揣湔善訧蹋踱麼紫黓孬.. 絞?褫眕唗蹈趙..憩珨隅褫眕毀唗蹈趙賸...?妗唗蹈趙跡宒
- asp.net如何得到GRIDVIEW中某行某列值的方法
- .net SMTP發(fā)送Email實(shí)例(可帶附件)
- js實(shí)現(xiàn)廣告漂浮效果的小例子
- asp.net Repeater 數(shù)據(jù)綁定的具體實(shí)現(xiàn)
- Asp.Net 無刷新文件上傳并顯示進(jìn)度條的實(shí)現(xiàn)方法及思路
- Asp.net獲取客戶端IP常見代碼存在的偽造IP問題探討
- VS2010 水晶報(bào)表的使用方法
- ASP.NET中操作SQL數(shù)據(jù)庫(kù)(連接字符串的配置及獲取)
- asp.net頁(yè)面?zhèn)髦禍y(cè)試實(shí)例代碼
- DataGridView - DataGridViewCheckBoxCell的使用介紹
- asp.net中javascript的引用(直接引入和間接引入)
- 三層+存儲(chǔ)過程實(shí)現(xiàn)分頁(yè)示例代碼
- 相關(guān)鏈接:
- 教程說明:
.Net教程-裝箱、轉(zhuǎn)型、方法調(diào)用他們究竟有什么區(qū)別?
。