在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)引用類型的實例創建在托管堆上。
下麵以一段代碼來詳細講解一下值類型與引用類型的區別
1 namespace Test 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 //調用ReferenceAndValue類中的Demonstration方法 8 ReferenceAndValue.Demonstration(); 9 Console.ReadLine(); 10 } 11 } 12 public class stamp //定義一個類 13 { 14 public string Name { get; set; } //定義引用類型 15 public int Age { get; set; } //定義值類型 16 } 17 public static class ReferenceAndValue //定義一個靜態類 18 { 19 public static void Demonstration() //定義一個靜態方法 20 { 21 stamp Stamp_1 = new stamp { Name = "Premiere", Age = 25 }; //實例化 22 stamp Stamp_2 = new stamp { Name = "Again", Age = 47 }; //實例化 23 int age = Stamp_1.Age; //獲取值類型Age的值 24 Stamp_1.Age = 22; //修改值類型的值 25 stamp guru = Stamp_2; //獲取Stamp_2中的值 26 Stamp_2.Name = "Again Amend"; //修改引用的Name值 27 Console.WriteLine("Stamp_1's age:{0}", Stamp_1.Age); //顯示Stamp_1中的Age值 28 Console.WriteLine("age's value:{0}", age); //顯示age值 29 Console.WriteLine("Stamp_2's name:{0}", Stamp_2.Name); //顯示Stamp_2中的Name值 30 Console.WriteLine("guru's name:{0}", guru.Name); //顯示guru中的Name值 31 } 32 } 33 }
通過運行上面一段程式之後我們可以看出,當改變了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,因此它們指向了同一塊記憶體區域。既然是指向同一塊區域,不管修改誰,另一個的值都會跟著改變,就像信用卡跟親情卡一樣,用親情卡取了錢,與之關聯的信用卡賬上也會跟著發生變化。
作者:耑新新,發佈於 博客園
轉載請註明出處,歡迎郵件交流:[email protected]