C#未分類菜單-『實值型別與參考型別的記憶體配置』

  • 9880
  • 0

摘要:C#未分類菜單-『實值型別與參考型別的記憶體配置』

~哀悼~ 給予不幸的人。

 

 
~本文主題~
『實值型別與參考型別』
『Stack與Heap記憶體配置說明』
 

 
~正文開始~

 

實值型別與參考型別
在C#中資料型別(Type)有分為兩種,『實值(Value)』與『參考(Reference)』,這種型別有何不同?
最簡單的區分法為
實質型別:即變數的記憶體(Stack)空間裡存放的為『內容(值)』。
參考型別:即變數的記憶體空間(Stack)裡存放的為『Heap記憶體位址』。
 
實質型別共有15種,如下表。
型態類別 關鍵字 位元數 範圍 預設值
整數 byte 8 0至255 0
整數 sbyte 8 -128至127 0
整數 short 16 -32,768至32,767 0
整數 ushort 16 0至65,535 0
整數 int 32 -2,147,483,648至2,147,483,647 0
整數 uint 32 0至4,294,967,295 0
整數 long 64 -9,223,372,036,775.808至9,223,372,036,775.807 0L
整數 ulong 64 0至18,446,744,073,709,551,615 0
浮點數 float 32 -3.402823e38至3.402823e38 0.0F
浮點數 double 64 -1.79769313486232e308至1.79769313486232e308 0.0D
浮點數 decimal 128 (-7.9 x 1028 至 7.9 x 1028) / (100 至 28) 0.0M
字元 char   Unicode字元 '\0'
字元 bool   True或false false
列舉 enum   自行訂義 由運算式 (E)0 所產生的值,而 E 是列舉識別項
結構 struct   自行訂義 這個值是由將所有實值型別欄位設定為其預設值,
且所有參考型別欄位設定為 
null 所產生
(參考資料:MSDN與程式語言教學日誌)
 
參考型別列表。
型態類別 關鍵字
類別 Class
字串 String
介面 Interface
委派 delegate
  dynamic
物件 object
(參考資料:MSDN)
 
Stack與Heap
.NET的CLR(備註)在記憶體中將資料分成兩個區域為『Stack』與『Heap』,這邊我們用個簡單的範例來說明,什麼東西該放入Stack而什麼東西又該放入Heap。我們範例將區分為兩種進行分別為實值型別(Int與Struct) 和 參考型別 (Class)。
 
實值型別(Value Type)範例
 
建立一個Car_Struct 結構來進行範例說明。

 struct  Car_Struct
    {
        private int speed;
        private int power;

        public Car_Struct(int speed, int power)
        {
            this.speed = speed;
            this.power = power;
        }

        public int Speed
        {
            get { return speed; }
            set { speed = value ; }
        }
        public int Power
        {
            get { return power; }
            set { power = value ; }
        }

        public void Run()
        {
            Console.WriteLine("My Car_Strucct Run ~~~Speed=" + speed.ToString() + " Power=" + power.ToString());
        }

    }

 private void button1_Click(object sender, EventArgs e)
        {
             Car_Struct mycar_st;
             mycar_st = new Car_Struct (10, 10);
             Car_Struct mycar_st_test;
             mycar_st_test = mycar_st;
              mycar_st_test.Speed = 20;
             mycar_st_test.Power = 20;
             mycar_st.Run();
   }

 

我們在Button的Click事件來進行這個結構的測試,並將說明每一行程式在記憶體區域的變化。
首先執行第一行 Car_Struct mycar_st;。如下圖,會先產生變數mycar_st,目前還是Null。
 
執行第二行,進行實體化,mycar_st會存放Car_Struct物件裡的內容。如下圖。

 mycar_st = new Car_Struct (10, 10);

 

然後在建立一個新的變數 mycar_st_test,則記憶體區域會為下圖,配值新的記憶體空間給mycar_st_test。

 Car_Struct mycar_st_test;

 

在將mycar_st傳至mycar_st_test 則,這行你可以想成複製整個內容過去。

mycar_st_test = mycar_st;

 

再修改mycar_st_test 的欄位,由於mycar_st_test為實值型別,與mycar_st算是不同的東西,因此
修改mycar_st_test 裡的欄位不會影響到mycar_st。如下圖。

 mycar_st_test.Speed = 20;
 mycar_st_test.Power = 20;

 

執行結果,如果該處執行mycar_st_test則結果就為Speed=20 , Power =20。

 mycar_st.Run();

 


 
參考型別(Reference Type)簡單範例說明
首先來簡單的建立一個Car類別。該類別與上面所建立的Struct內容相同。

class Car
    {
        private int speed;
        private int power;

        public Car(int speed,int power)
        {
            this.speed = speed;
            this.power = power;
        }

        public void Run()
        {
            Console.WriteLine("My Car Run ~~~" + speed.ToString() + power.ToString() );
        }

    }


 private void button1_Click(object sender, EventArgs e)
        {
            Car mycar;
            mycar = new Car (10,10);

            Car testcar;
            testcar = mycar ;
            testcar.Power = 20;
            testcar.Speed = 20;

             mycar.Run();
}

 

在還沒執行net之前,CLR就會在Stack裡配置記憶體區塊,而裡面還是
Null,同時Heap裡只會有Car Type Object 但並不是我們New欄位有資料的
那個物件。

 Car mycar;

類別第一次使用時,會在Heap會產生一個屬於該類別的Type Object。
該Type Object只會產生一次,而且會一直存在到AppDomain被卸載。
(感謝Bill Chung哥在第一篇物件導向的指導)
(注:想知道Type Object裡那幾個名詞是啥的可以參考91哥的文章)
 
注意:
在這範例的mycar是一個參考,而不是物件本身喔
 
 
然後在進行NEW過後,記憶體區域會變成下圖,在mycar裡會存放
Heap記憶體區域的Car object位址。

 mycar = new Car (10,10);

 

我們在執行 testcar = mycar ; 時記憶體區塊會變成下圖,感覺就像是把
位址傳給testcar參考。也可以說testcar與mycar是連結到同樣的Heap
記憶體位址。

 Car testcar;
 testcar = mycar ;

 

然後我們在修改testcar的欄位,則記憶體區塊域,變為下圖。修改的
為同一個Car Object,所以我們這時呼叫mycar.Run()方法時,應該
是會呼叫出修改過後的欄位值。

    testcar.Power = 20;
     testcar.Speed = 20;

 

執行結果,如果是執行testcar.Run()則結果相同。

mycar.Run();

 

總結
整理一張上面程式碼的對比。
程式碼說明 Class 類別 Struct 結構 類別
記憶體區塊變化
結構
記憶體區塊變化
新增變數(1) Car mycar; Car_Struct mycar_st; 新增一個存位罝的參考
預設Null
新增一個存內容的
變數。預設Null
進行實體化 mycar = new Car (10,10); mycar_st = new Car_Struct (10, 10); Heap中產生Object Stack變數產生內容
新增另一變數(2) Car testcar; Car_Struct mycar_st_test; 新增一個存位罝的參考
。預設Null。
新增一個存內容的
變數。預設Null。
將(1)傳至(2) testcar = mycar ; mycar_st_test = mycar_st; 將Heap記憶體位址傳
給mycar。
(好像說複製比較好)
將mycar_st的內容
複製給mycar_st_test
修改(2)的欄位。 testcar.Power = 20;
testcar.Speed = 20;
mycar_st_test.Speed = 20;
mycar_st_test.Power = 20;
修改testcar的欄位。 修改Heap 對應位址的Car Objet 內容。
執行(1)的方法。 mycar_st.Run(); mycar.Run(); 呼叫內容為存放於
mycar_st 。
呼叫內容為存放於Heap對應位址
的Car Object 物件內容。
 
 
 
 
備註:
 
什麼是CLR
.NET Framework 提供稱為 Common Language Runtime 的執行階段環境,它能執行.NET的程式碼,並提供使程式開發過程更為容易的服務。
以下為通用語言執行平臺。
 

 

 

 



小弟才書學淺~請各位客官指教指教~~~

小弟日 : 你要知道自已不是帥哥,你才有可能變成帥哥 。