值型別與引用型別淺析
1、 總概括:
值型別就是現金,要用直接用;引用型別是存摺,要用還得先去銀行取現。
2、值型別與引用型別簡介
(1)C#的所有值型別均派生自System.ValueType:
結構體:
數值型別:整型(byte、short、int、long、char等)、浮點型(float、double)、decimal型(decimal)
bool型
使用者定義的結構體
列舉:enum
可空型別(System.Nullable<T>泛型結構體) 例如:int? a
總結:值型別(ValueType),值型別例項通常分配線上程的堆疊(stack)上,並且不包含任何指向例項資料的指標,因為變數本身就包含了其例項資料
(2)C#有以下一些引用型別:
陣列(派生於System.Array)
使用者用定義的以下型別:類(派生於System.Object)、介面、委託(派生於System.Delegate)
object(System.Object的別名);
字串:string(System.String的別名)
String是比較特殊的引用型別:每次建立一個string時,都會重新再託管堆上重新分配新的記憶體空間,所以進行賦值操作的時候,原string的值不變。(可以比較stringBuilder與string)
String的缺點是每次字串變數的內容發生了改變時,都必須重新分配記憶體。StringBuilder通過分配一個快取,新增,刪除,移除,插入和替換字元等等。執行完之後,將呼叫ToString方法把工作區中的內容轉換為一個字串,方便賦給一個字串變數。這樣StringBuilder會提升一些效能。
(3)相同點與不同點:
引用型別可以派生出新的型別,而值型別不能;
引用型別變數的賦值只複製對物件的引用,而不復制物件本身。而將一個值型別變數賦給另一個值型別變數時,將複製包含的值;
3、資料在記憶體中分配位置取決與該變數的資料型別,值型別分配線上程的堆疊上,引用型別則分配在託管堆上,由GC控制回收。
下面是個經典的例子:
private static class ReferenceVsValue
{
//引用型別(class關鍵字)
private class SomeRef { public Int32 x; }
//值型別(struct關鍵字)
private struct SomeVal { public Int32 x; }
public static void Go()
{
SomeRef r1 = new SomeRef(); //在堆上分配
SomeVal v1 = new SomeVal(); // 在棧上分配
r1.x = 5; // 提領指標
v1.x = 5; // 在棧修改
Console.WriteLine(r1.x); // 顯示”5”
Console.WriteLine(v1.x); //同樣顯示”5”
// 下圖左半部分反映了執行以上程式碼之後的情形
SomeRef r2 = r1; //只複製引用(指標)
SomeVal v2 = v1; // 在棧上分配並且複製成員
r1.x = 8; // r1.x和r2.x都會更改
v1.x = 9; // 只是更改v1.x,不會更改v2.x
Console.WriteLine(r1.x); // 顯示 "8"
Console.WriteLine(r2.x); // 顯示 "8"
Console.WriteLine(v1.x); // 顯示 "9"
Console.WriteLine(v2.x); // 顯示 "5"
//右半部分反映了在執行所有程式碼之後的情況
}
}