C# 值類型和引用類型


一、基本概念

C#只有兩種數據類型:值類型和引用類型

值類型在線程棧分配空間,引用類型在托管堆分配空間

值類型轉為引用類型稱成為裝箱,引用類型轉為值類型稱為拆箱

以下是值類型和引用類型對照表

從上圖可以簡單看出:string,Object,數組,class是引用類型,簡單類型,枚舉,結構是值類型。

二、代碼展示

定義一個類和結構調用賦值

內存分配情況如下圖:

從這張圖可以看出,class實例化出來的對象,指向了內存堆中分配的空間;truct實例化出來的對象,是在內存棧中分配。

修改代碼如下:

內存分配情況:

由上圖可以知:

object obj=”abc”;  
string i=(string)obj;  

值類型和引用類型儲的位置不一樣
如果是引用類型,當兩個對象指向同一個地方,修改某一個的時候,其它對象的值會發生改變

注意點:

1、值類型變量做為局部變量時,該實例將被創建在堆棧上;而如果值類型變量作為類型的成員變量時,它將作為類型實例數據的一部分,同該類型的其他字段都保存在托管堆上。

2、引用類型變量數據保存在托管堆上,但是根據實例的大小有所區別:當實例的大小小於85000Byte時實例將創建在GC堆上;當實例大小>=85000byte時,則該實例創建在LOH(Large Object Heap)堆上。

using System;

namespace ConsoleApplication2
{
    //引用類型(因為‘class’)
    public class SomeRef
    {
        public int x { get; set; }
    }
    //值類型(因為‘struct’)
    public struct SomeVal
    {
        public int x{ get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            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 = 8;       //v1.x會更改,v2.x不會

            Console.WriteLine(r1.x); //顯示8  
            Console.WriteLine(r2.x); //顯示8  
            Console.WriteLine(v1.x); //顯示8
            Console.WriteLine(v2.x); //顯示5

            Console.ReadKey();
        }
    }
}
圖片上的代碼

 棧stack(先進后出)是編譯期間就分配好的內存空間,因此你的代碼中必須就棧的大小有明確的定義;

 堆heap(隊列優先,先進先出)是程序運行期間動態分配的內存空間,你可以根據程序的運行情況確定要分配的堆內存的大小。

二、值類型裝箱、拆箱

1、裝箱:就是將值類型的數據打包到引用類型的實例中( 比如將int類型的值1賦給object對象obj)

int  i=1;  
object obj=(object)i;  

 裝箱時到底發生的事情:

1)、在托管堆中分配內存。分配的內存量是值類型各字段所需的內存量,還要加上托管堆所有對象都有的兩個額外成員(類型對象指針和同步塊索引)所需的內存量

2)、值類型的字段復制到新分配的堆內存

3)、返回對象地址,現在該地址是對象引用;值類型成了引用類型

2、拆箱:就是從引用數據中提取值類型(比如將object對象obj的值賦給int類型的變量i)

object obj=”1”;  
int i=(int)obj;  

拆箱不是裝箱過程倒過來,拆箱的代價要比裝箱低的多,拆箱是獲取指針的過程,該指針指向包含在一個對象中的原始值類型。

拆箱后將堆中的字段包含的值復制棧的值類型實例中。

3、值類型變為引用類型不一定要裝箱,例如:

string str = "joye.net" + 26;   //需要將26裝箱為string類型
string str1 = "joye.net" + 26.ToString(); //tostring后不需要裝箱

感興趣的可以用IL看一下。

三、參考資料

  《深入理解C#》第二版

  《CLR VIA C#》第四版

  《C# 高級編程》第四版

  還有很多網絡上的文章,就不一一例舉了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM