這篇文章主要為大家詳細介紹了C#中實值型別和參考型別的相關資料,具有一定的參考價值,感興趣的小夥伴們可以參考一下
在C#中,實值型別和參考型別是相當重要的兩個概念,必須在設計類型的時候就決定類型執行個體的行為。如果在編寫代碼時不能理解參考型別和實值型別的區別,那麼將會給代碼帶來不必要的異常。很多人就是因為沒有弄清楚這兩個概念從而在編程過程中遇到了很多問題,在這裡博主淺談對實值型別和參考型別的認識。
首先從概念上看,實值型別直接儲存其值,而參考型別儲存對其值的引用。從而這兩種類型儲存在記憶體的不同地方。
其次從記憶體空間上看,實值型別是在棧中操作,而參考型別則在堆中分配儲存單元。
棧在編譯的時候就分配好記憶體空間,在代碼中有棧的明確定義,而堆是程式運行中動態分配的記憶體空間,可以根據程式的運行情況動態地分配記憶體的大小。因此,實值型別總是在記憶體中佔用一個預定義的位元組數。而參考型別的變數則在棧中分配一個記憶體空間,這個記憶體空間包含的是對另一個記憶體位置的引用,這個位置是託管堆中的一個地址,即存放此變數實際值的地方。
也就是說實值型別相當於現金,要用就直接用,而引類型相當於存摺,要用得先去銀行取。
但實值型別在棧上分配記憶體,而參考型別在託管堆上分配記憶體,只是一種籠統的說法。下面對其進行詳細描述。
(1)對於實值型別的執行個體,如果作為方法中的局部變數,則被建立線上程棧上;如果該執行個體作為類型的成員,則作為類型成員的一部分,連同其他類型欄位存放在託管堆上。
每種實值型別均有一個隱式的預設建構函式來初始化該類型的預設值。例如:
int i = new int();
等價於:
Int32 i = new Int32();
等價於:
int i = 0;
等價於:
Int32 i = 0;
使用new運算子時,將調用特定類型的預設建構函式並對變數賦以預設值。在上例中,預設建構函式將值0賦給了i。
說明:C#的所有實值型別均隱式派生自System.ValueType,而System.ValueType直接派生於System.Object。即System.ValueType本身是一個類類型,而不是實值型別。其關鍵在於ValueType重寫了Equals方法,從而對實值型別按照執行個體的值來比較,而不是引用地址來比較。
(2)參考型別的執行個體建立在託管堆上。
下面以一段代碼來詳細講解一下實值型別與參考型別的區別
namespace Test { class Program { static void Main(string[] args) { //調用ReferenceAndValue類中的Demonstration方法 ReferenceAndValue.Demonstration(); Console.ReadLine(); } } public class stamp //定義一個類 { public string Name { get; set; } //定義參考型別 public int Age { get; set; } //定義實值型別 } public static class ReferenceAndValue //定義一個靜態類 { public static void Demonstration() //定義一個靜態方法 { stamp Stamp_1 = new stamp { Name = "Premiere", Age = 25 }; //執行個體化 stamp Stamp_2 = new stamp { Name = "Again", Age = 47 }; //執行個體化 int age = Stamp_1.Age; //擷取實值型別Age的值 Stamp_1.Age = 22; //修改實值型別的值 stamp guru = Stamp_2; //擷取Stamp_2中的值 Stamp_2.Name = "Again Amend"; //修改引用的Name值 Console.WriteLine("Stamp_1's age:{0}", Stamp_1.Age); //顯示Stamp_1中的Age值 Console.WriteLine("age's value:{0}", age); //顯示age值 Console.WriteLine("Stamp_2's name:{0}", Stamp_2.Name); //顯示Stamp_2中的Name值 Console.WriteLine("guru's name:{0}", guru.Name); //顯示guru中的Name值 } } }
通過運行上面一段程式之後我們可以看出,當改變了Stamp_1.Age的值時,age並沒有跟著變,但在改變了anders.Name的值後,guru.Name卻跟著變了,這就是實值型別和參考型別的區別。在聲明age實值型別變數時,將 Stamp_1.Age的值賦給它,這時,編譯器在棧上分配了一塊空間,然後把Stamp_1.Age的值填進去,二者沒有任何關聯,就像在電腦中複製檔案一樣,只是把Stamp_1.Age的值拷貝給age了。而參考型別則不同,在聲明guru時把Stamp_2賦給它,前面說過,參考型別包含的只是堆上資料區域地址的引用,其實就是把Stamp_2的引用也賦給guru,因此它們指向了同一塊記憶體地區。既然是指向同一塊地區,不管修改誰,另一個的值都會跟著改變,就像信用卡跟親情卡一樣,用親情卡取了錢,與之關聯的信用卡賬上也會跟著發生變化。