.net中裝箱的幾種情況
本篇所寫都是本人想當然的理解。如果這種理解便于幫助你理解一些知識的話,我會感到由衷的欣慰。
.net中雖然沒有指針語法,但是在堆中分配對象,將引用放在棧中,十分類似C++中的指針操作,此時引用就可以看成一種特殊的指針。因為指針操作的間接性,會帶來一定的性能影響,為了避免這種影響,.net采取了一種折衷的辦法,引入了值類型。
為了在值類型和引用類型之間進行一些合理的轉換,于是帶來了裝箱和拆箱。
裝箱簡單來說就是將值類型轉換為引用類型。按三步進行:
(1)新分配托管堆內存(大小為值類型實例大小加上一個方法表指針和一個SyncBlockIndex)。
(2)將值類型的實例字段拷貝到新分配的內存中。
(3)返回托管堆中新分配對象的地址。
拆箱就是將引用類型轉換為對應的值類型。分如下步驟進行:
(1)檢查引用對象實例,確保它是給定值類型的一個裝箱值。
(2)獲取引用對象中指向值類型部分的指針。
(3)將引用對象中對應的內容拷貝到值類型區域。
從上面步驟可以看出,裝箱和拆箱會給程序的性能帶來一定的影響,所以我們應盡可能地避免裝箱和拆箱。裝箱可以隱式進行,拆箱只能顯式進行。只有先裝箱,才能拆箱。
為了盡可能地避免裝箱和拆箱,我們需要了解裝箱的幾種情況。
我總結了以下幾種(可能不太全面):
(1)方法中參數為Object類,但是傳遞一個值類型。
void f(object obj)2


{3

4
}5

6
static void Main(string[] args)7


{8
f(32);9
}
(2)一個類型中有field申明為Object類,賦予一個值類型。
class Container2

{3
private object m_obj;4

5
public object Obj6

{7

get
{ return m_obj; }8

set
{ m_obj = value; }9
}10

11
}12
13

14
class Program15

{16
static void Main(string[] args)17

{18
Container con;19
//這里會發生裝箱20
con.Obj = 45;21
}22
}
(3)調用Object類中沒有被值類型覆蓋的方法,如GetType()。
2
//值類型,這里借用《.net框架程序設計》中的例子,并做了適當修改3
struct MyValType4

{5
RefType refobj; //引用類型6
ValType valobj; //值類型7

8
public override bool Equals(object obj)9

{10
//這里如果這樣寫,this.GetType(),會將this裝箱。11
//因為MyValType沒有覆寫GetType()方法,會實際使用Object的GetType()方法12
//如果要使用GetType()方法,必須先構建方法表,于是發生裝箱13
if (this.GetType() != obj.GetType()) 14
return false;15
return this.Equals((MyValType)obj);16
}17

18
public Boolean Equals(MyValType obj)19

{20
if (!Object.Equals(this.refobj,obj.refobj))21
return false;22
if (!this.valobj.Equals(obj.valobj))23
return false;24
return true;25
}26
}
(4)將值類型轉換為成一個被該值類型實現的接口類型。
interface IChange2

{3
void Change(System.Int32 x);4
}5

6
struct MyValType: IChange7

{8
private int value;9

10
public int Value11

{12

get
{ return this.value; }13

set
{ this.value = value; }14
}15

16
public void Change(System.Int32 x)17

{18
value = x;19
}20
}21

22
class Program23

{24
static void Main(string[] args)25

{26
MyValType valType = new MyValType();27
valType.Value = 10;28
//此時會發生裝箱29
IChange iChange = valType;30
//此時修改,是修改堆中的內存,不會修改valType31
iChange.Change(20);32

33
//拆箱34
MyValType valType2 = (MyValType)iChange;35

36
//輸出10,valType.Value在iChange.Change(20)時不會改變37
System.Console.WriteLine(valType.Value);38
//valType2.Value為2039
System.Console.WriteLine(valType2.Value);40
System.Console.Read();41
}42
}針對以上四種情況,為了減少裝箱和拆箱,建議以如下形式進行:
(1)方法中參數為Object類,但是傳遞一個值類型。 建議利用方法重載或者泛型。
(2)一個類型中有field申明為Object類,賦予一個值類型。 建議利用泛型。
(3)調用Object類中沒有被值類型覆蓋的方法,如GetType()。
根據實際情況,判斷是否有其它方法實現,如上面舉的例子就可以這樣修改:
//值類型2
struct MyValType3

{4
RefType refobj; //引用類型5
ValType valobj; //值類型6

7
public override bool Equals(object obj)8

{9
//這里不用GetType(),可以避免裝箱10
//同時,因為值類型不能有子類,所以這里用is就可以達到類型比較的目的11
if (!(obj is MyValType))12
return false;13
return this.Equals((MyValType)obj);14
}15

16
public Boolean Equals(MyValType obj)17

{18
if (!Object.Equals(this.refobj,obj.refobj))19
return false;20
if (!this.valobj.Equals(obj.valobj))21
return false;22
return true;23
}24
}
(4)將值類型轉換為成一個被該值類型實現的接口類型。如果設計上真要求這么做,那可能只能如此了。我暫時沒有想到什么解法,如果你有更好的解法,希望不吝賜教。
posted on 2009-04-27 23:24 五味雜陳 閱讀(1779) 評論(1) 編輯 收藏 引用 所屬分類: .NET

