[TOC]
C#學習筆記之值型別與引用型別
1.值型別與引用型別
1.1 深層區別
值型別與引用型別有不同的記憶體分佈,這導致了不同的記憶體管理機制:
- 值型別由OS負責記憶體管理
- 引用型別由垃圾回收器(GC)負責記憶體管理
記憶體管理:指的是對記憶體的分配與釋放的管理
1.2 值型別與引用型別的巢狀使用
1.2.1 引用型別巢狀值型別
eg 1.類的欄位值是值型別,它是在託管堆上的。
eg 2.區域性變數(如方法中的變數),是在堆疊上的。
1.2.2 值型別巢狀引用型別
堆疊儲存了引用,託管堆儲存了實際的資料。
1.3 小結
- 由於值型別由OS直接對記憶體進行管理,而引用型別需要使用託管堆對記憶體進行管理,所以值型別在效能上有天然的優勢;
- 引用型別可以具有繼承性(類,介面);
- 使用引用型別進行傳參時會改變變數本身(string除外,因為string具有不變形,賦值後不可改變。可以理解為const修飾符修飾的變數?);
- 值型別例項總會被分配到它宣告的地方,引用型別總被分配到託管堆上。
2.裝箱與拆箱
2.1 型別轉換的幾種方法
- 隱式的型別轉換(裝箱);
- 顯示轉換
- is和as運算子進行安全的型別轉換
- .NET類庫中的Conver進行型別轉換
2.2 什麼是裝箱與拆箱
graph TD;
值型別–>1裝箱;1裝箱–>引用型別;引用型別–>2拆箱;2拆箱–>值型別;
值型別–>1裝箱;1裝箱–>引用型別;引用型別–>2拆箱;2拆箱–>值型別;
值型別裝箱成為引用型別,引用型別拆箱成為值型別。
裝箱過程:在託管堆生成一份堆疊中值型別物件的備份。
- 記憶體分配:在託管堆中分配放置賦值的實際資料的空間;
- 完成實際資料的分配:將值型別例項的資料複製到新分配的空間中;
- 地址返回:將託管堆中的物件地址返回給引用型別變數。
拆箱過程:將託管堆中生成的引用型別所指向的已裝箱的值型別資料複製到值型別物件的過程。
- 檢查例項:是否為null,否則繼續檢查變數是否和拆箱後的型別是否為同一型別,是繼續;
- 地址返回:返回已經裝箱變數的實際資料部分地址;
- 資料複製:將託管堆中的資料複製到堆疊中。
2.3 一個裝箱與拆箱的例子
int i = 3;
object o = i; // 裝箱
int y = (int)o; // 拆箱
2.4 裝箱與拆箱帶來的問題
- 效能問題
- 中間帶有複製操作,會降低效能
- 產生中間物件,造成了GC(垃圾回收器)的負擔
- 會產生隱藏的bug
2.5 小結
- 值型別裝箱成為引用型別,引用型別拆箱成為裝箱前的值型別
- 裝箱會在託管堆生成堆疊中值型別物件的備份,根據這個備份可以完成拆箱
- 過多使用裝箱與拆箱操作會帶來效能問題與產生一些難以排除的bug