值型別的拆箱與裝箱

2021-08-18 18:06:52 字數 2357 閱讀 4475

在之前文章中提到了,值型別具有兩種表現形式:已裝箱和未裝箱,這兩種狀態的轉換過程稱之為裝箱和拆箱。從記憶體分配的角度來說,裝箱就是將值型別經過處理從執行緒棧複製到託管堆;拆箱則是將已裝箱的值型別例項從託管堆複製到執行緒棧。

裝箱流程:

在託管堆中分配記憶體,記憶體大小 = 值型別大小 + 物件指標 + 同步塊索引。

逐字段將值型別複製到新分配的記憶體。

返回物件指標,指標指向新分配的記憶體,至此,值型別轉換成了引用型別。

拆箱流程:

獲取已裝箱物件中各個欄位的位址,這個過程稱之為拆箱,在這個過程開始時會對已裝箱物件進行檢查,首先檢查是否為null,如果為null,丟擲nullreferenceexception;然後檢查已裝箱物件是否為所轉值型別,如果不是則丟擲invalidcastexception。至此拆箱操作已經完成了,拆箱其實就是獲取字段指標的過程,但是一般緊接著都會發生一次字段複製,所以也將字段複製考慮到拆箱效能損耗。

將字段逐一從託管堆複製到執行緒棧中。

為了避免裝箱和拆箱產生效能損耗,首先我們需要知道什麼時候我們寫的**會發生裝箱操作,下面主要列舉四種情形。

最容易發現的一種情況是顯示轉換為object型別

//值型別強轉object型別時,發生裝箱

int number = 10;

object boxednumber = number;

還有一種比較容易忽略的情況,值型別作為object型別實參,所以很多方法過載多個版本來減少值型別的裝箱和拆箱

int number = 10;

//呼叫console.writeline(string,object)函式時,因為第二個引數需要object型別,

//所以需要將number進行裝箱

console.writeline("box number:",number);

這是因為介面變數必須包含對堆物件的引用。

同樣也包括兩種情況,一種是顯示轉換,第二種是作為實參進行傳遞。

//隨意定義

public

inte***ce idosomething{}

//自定義結構,實現介面

public

struct vector3 : idosomething

public

class test

}

在呼叫值型別例項的基類方法(gettype、memberwiseclone、tostring、gethashcode、equals)時,會造成裝箱,原因是在訪問基類方法時,需要基類方法由system.object型別定義,要求this實參是指向託管堆的指標,但是有一種特殊的情況不會造成裝箱,那就是,在呼叫基類virtual方法時,如果override方法沒有呼叫base方法,就不會發生裝箱,下面通過**來詳細看下。

public

struct vector2

}//顯式呼叫基類方法

public

void

testbasecall()

如果定義的型別重寫了基類的virtual方法,並且override方法中沒有呼叫base,則不會造成裝箱

public

struct vector2

//重寫基類的tostring方法,並且不能呼叫base.tostring

public

override

string

tostring()

,)",_x.tostring(),_y.tostring());

}}public

void

testvirtualcall()

除了以上這些顯示的呼叫,在一些類的實現當中,也會涉及到基類方法的呼叫,比如dictionary,hashtable需要呼叫物件的gethashcode計算雜湊碼,如果沒有重寫gethashcode就會造成裝箱,解決辦法是重寫gethashcode方法和equals方法。

public

struct vector2

//重寫基類的tostring方法,並且不能呼叫base.tostring

public

override

string

tostring()

,)",_x.tostring(),_y.tostring());

}}public

void

testbasecall()

至此,已將將常見的裝箱情況總結完了,在平時編碼過程中,如果對一些**是否會發生裝箱、拆箱不太確定,可以使用反編譯工具,檢視il編碼來檢視是否包含box語句來判斷,這裡推薦使用開源工具ilspy進行檢視。

值型別的裝箱,拆箱

裝箱發生的事情 1.在託管堆中分配記憶體。分配的記憶體量是值型別各字段所需的記憶體量,還要加上託管堆所有物件都有的兩個額外成員 型別物件指標和同步塊索引 所需的記憶體量。2.值型別的字段複製到新分配的堆記憶體。拆箱發生的事情 1.獲取已裝箱物件中各個欄位的位址。2.將字段包含的值從堆複製到基於棧的值...

值型別 引用型別,裝箱 拆箱

值型別 宣告乙個值型別變數,會在棧上分配乙個空間,空間裡儲存的就是變數的值 引用型別 宣告乙個引用型別變數,會在棧中分配乙個空間,儲存乙個引用,這個引用指向了乙個託管堆。值型別 struct,列舉,數值型別,bool型別 引用型別 陣列,類,介面,委託 delegate object,string ...

值型別與引用型別區別and裝箱與拆箱

1.引用型別的內存在託管堆上分配的。new操作符會返回物件的記憶體位址。2.值型別比引用型別輕量級 值型別包含啥呢,結構和列舉 3.值型別是不能作為基類 4.gc不管值型別 5.引用型別沒有初始化時是null,即不指向任何的記憶體位址,如果使用會拋異常nullreference異常 值型別的初始值是...