摘要: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的程式碼,並提供使程式開發過程更為容易的服務。
以下為通用語言執行平臺。
參考資料