1. 裝箱轉換
裝箱轉換是指將一個值類型隱式地轉換成一個object 類型,或者把這個值類型轉換成一個被該值類型應用的接口類型interface-type。把一個值類型的值裝箱,也就是創建一個object 實例並將這個值復制給這個object。比如:
int i = 10;
object obj = i;
用下圖所示,可以表示裝箱的過程
裝箱的過程
我們也可以用顯式的方法來進行裝箱操作:
int i = 10;
object obj =(object)i;
2、拆箱轉換
和裝箱轉換正好相反,拆箱轉換是指將一個對象類型顯式地轉換成一個值類型,或是將一個接口類型顯式地轉換成一個執行該接口的值類型。
拆箱的過程分為兩步:首先,檢查這個對象實例,看它是否為給定的值類型的裝箱值。然后,把這個實例的值拷貝給值類型的變量。
利用如下例子,查看一個對象拆箱的過程 。
int i = 10;
object obj = i;
int j = (int)obj;
拆箱的過程用圖來表示就是:
拆箱的過程
可以看出拆箱過程正好是裝箱過程的逆過程。必須注意,裝箱轉換和拆箱轉換必須遵循類型兼容原則。
盡量避免裝箱
我們之所以研究裝箱和拆箱,是因為裝箱和拆箱會造成相當大的性能損耗(相比之下,裝箱要比拆箱性能損耗大),性能問題主要體現在執行速度和字段復制上。因此我們在編寫代碼時要盡量避免裝箱和拆箱,常用的手段為:
1. 使用重載方法。為了避免裝箱,很多FCL中的方法都提供了很多重載的方法。比如我們之前討論過的Console.WriteLine方法,提供了多達19個重載方法,目的就是為了減少值類型裝箱的次數。比如看下面的這段代碼:
Console.WriteLine(3);
剛開始你可能覺的3會裝箱為string類型,但是實際上這條語句不會進行裝箱操作,是因為Console.WriteLine方法有一個重載的方法,參數就是一個int的值。
public static void WriteLine(int value);
類似Console.WriteLine方法,還有System.IO.BinaryWriter的Write 方法,System.IO.TextWriter 的Write和WriteLine方法,System.Text.StringBuilder的Append和Insert方法等都提供了大量的重載方 法,以減少裝箱次數。所以我們在實際的項目中,應該時刻注意裝箱的情況,並且選用合適的重載方法避免裝箱。
2. 使用泛型。因為裝箱和拆箱的性能問題,所以在.NET 2.0中引用了泛型,他的主要目的就是避免值類型和引用類型之間的裝箱和拆箱。我們常用的集合類都有泛型的版本,比如ArrayList對應着泛型的 List<T>,Hashtable對應着Dictionary<TKey, Tvalue>。
3. 如果在項目中一個值類型變量需要多次拆裝箱,那么可以將這個變量提出來在前面顯式裝箱。比如下面這段代碼:
int j = 3;
ArrayList a = new ArrayList();
for (int i = 0; i < 100; i++)
{
a.Add(j);
}
// 可以修改為:
int j = 3;
object ob = j;
ArrayList a = new ArrayList();
for (int i = 0; i < 100; i++)
{
a.Add(ob);
}
4. ToString。這點單獨列出來是因為雖然小,但是很實用。雖然表面上看值類型調用ToString方法是要進行裝箱的,因為ToString是從基類 繼承的方法。但是ToString方法是一個虛方法,值類型一般都重寫了這個方法,所以調用ToString方法不會裝箱。之前說過String.Format方法容易造成裝箱,避免的最佳方法就是在調用這個方法前將所有的值類型參數都調用一次ToString方法。