實值和參考型別 (Visual C# Express)
更新:2007 年 11 月
與您熟悉的某些程式設計語言不同的是,C# 有兩種資料型別:實值和參考。如果應用程式的效能很重要,或是如果您對 C# 如何管理資料和記憶體有興趣,了解兩者之間的差別就很重要。
當變數是以基本內建資料型別其中之一,或使用者定義的結構進行宣告時,此變數便是實值型別 (Value Type)。有一個例外是 string 資料型別,它屬於參考型別。
實值型別會將內容儲存在配置於堆疊的記憶體中。例如,在這個案例中,值 42 會儲存在稱為堆疊的記憶體區域中:
int x = 42;
當變數 x 因為所定義的方法完成執行而超出範圍時,堆疊就會捨棄此值。
使用堆疊很有效率,但是實值型別有限的存留期,使其較不適合在不同類別之間共用資料。
相反地,參考型別 (例如類別或陣列的執行個體) 會配置在稱為堆積 (Heap) 的不同記憶體區域中。在下列範例中,組成陣列的十個整數所需要的空間會配置在堆積中。
int[] numbers = new int[10];
當方法完成時,這段記憶體不會歸還給堆積,只有當 C# 的記憶體回收系統判定不再需要它時才會回收。在宣告參考型別時需要較大的額外負荷,但是它們有可以從其他類別存取的優點。
Boxing 與 Unboxing
Boxing 是指定給將實值型別轉換為參考型別的處理序名稱。當您 box 某個變數時,就會建立指向堆積中新複本的參考變數。參考變數是一個物件,因此可以使用每個物件繼承的所有方法。例如,ToString()。這就是以下程式碼的內容:
int i = 67; // i is a value type
object o = i; // i is boxed
System.Console.WriteLine(i.ToString()); // i is boxed
當您使用設計為配合物件使用的類別時,便會遇到 unboxing:例如,使用 ArrayList 儲存整數。將整數存入 ArrayList 中時,就會被 box 為物件。而從 ArrayList 中讀取時,物件會被 unbox 為整數。
System.Collections.ArrayList list =
new System.Collections.ArrayList(); // list is a reference type
int n = 67; // n is a value type
list.Add(n); // n is boxed
n = (int)list[0]; // list[0] is unboxed
效能問題
讓我們再深入一點探討。當資料傳遞至方法當做實值型別參數時,就會在堆疊上建立每個參數的複本。很明顯地,如果參數是一個大型資料型別,例如使用許多項目之使用者定義結構,或是執行許多次的方法,這可能會對效能造成影響。
在上述情況中,建議使用 ref 關鍵字將參考傳遞到型別。這是將變數指標傳遞給函式的 C++ 技術,在 C# 的對等用法。當使用 C++ 版本時,方法能夠變更變數的內容,這個動作不是永遠都安全。程式設計人員需要在安全性和效能之間權衡得失。
int AddTen(int number) // parameter is passed by value
{
return number + 10;
}
void AddTen(ref int number) // parameter is passed by reference
{
number += 10;
}
out 關鍵字類似 ref 關鍵字,但是會告訴編譯器方法必須指派一個值給參數,否則會發生編譯錯誤。
void SetToTen(out int number)
{
// If this line is not present, the code will not compile.
number = 10;
}