C#基礎_值類型和引用類型

C#將數據類型分爲兩種:值類型和引用類型。

這兩種類型存儲在內存的不同的地方:值類型存儲在堆棧(Stack)中,而引用類型則存儲在託管堆(managed)上。區分類型是值類型還是引用類型非常重要,這會造成不同的結果。在瞭解上面的知識之前首先應該弄清楚兩個問題。

1.什麼是棧?

棧可以理解爲一種內存結構,它是先進後出的,就像桶裝薯片一樣,放進去的時候是一個接一個壓進去的,在這裏我們成爲壓棧,而要去拿的時候都是從最後一個進去的開始那。這就是它的特點:先進後出。

在語言中比如我們的程序入口Main方法中有一個GetMoney的方法。它的執行順序就是先將Main方法壓棧再將GetMoney方法壓棧,那麼執行的時候就是先執行GetMoney方法,執行完畢之後再執行Main方法,這也是棧的應用。

A:棧只能在一端進行操作,也就是棧頂,因爲棧底是封閉的。

B:棧不需要開發人員進行管理內存,壓棧的時候自行分配內存,出棧的時候自行清理內存。

C:棧的可用空間不大,因此它的執行效率很高,它不能動態請求內存,只能夠爲內存大小確定的數據分配內存。

2.什麼是堆?

A:堆在C#中用於存儲實例對象,對象可能會有很多數據,因此它的第一個特性就是:容量大能夠存儲很多數據,而且能夠動態的分配存儲空間。

B:棧我們已經知道了只能在棧頂一端進行操作,但堆就不一樣了,它可以隨意的存取,非常靈活。

C:事情都是有雙面性的,堆的內存空間既然大能夠存儲的數據多,那麼它的執行效率肯定是沒有堆高的。

一:那麼值類型和引用類型到底是怎麼樣在內存中分配的?

A:對於值類型來說:其變量對應的值是存放在棧中。如果該值類型的實例作爲類型的成員,而該實例作爲引用類型的一部分的時候,則它被創建在GC堆上。

在這裏插入圖片描述

B:對於引用類型來說:一塊空間被分配在堆上,存儲應用本身的數據,而另外一塊則被分配在棧上,存儲對 堆上數據的引用(其實就是內存地址 也叫做指針)例如Person p = new Person();這裏可以分爲兩部分來理解。Person p相當於定義了對象的引用,也就是記錄了對象實例的指針,而不是對象本身。這個引用存儲在棧中,當沒有使用p = new Person()的時候,引用本身爲空也就是相當於指針沒有指向任何位置;當p = new Person()後,才根據真正的對象的大小動態的在堆中分配空間給對象實例,然後會將實例的引用賦值給p。到這裏纔算是完成了一個對象的實例化。如下圖:

在這裏插入圖片描述

在C#中還有一種概念叫按值傳遞和按引用傳遞?

1.什麼時按值傳遞?我們先來看兩段代碼。

    public struct GameStateStruct
    {
        public int a { get; set; }
        public int b { get; set; }
    }
    public class GameStateClass
    {
        public int a { get; set; }
        public int b { get; set; }
    }

1.定義了一個結構體 名稱爲GameStateStruct

2.定義了一個類 名稱爲GameStateClass

3.在Main方法中操作如下

var tempStract1 = new GameStateStruct();
            tempStract1.a = 1;
            tempStract1.b = 1;
            //var tempClass1 = new GameStateClass();
            var tempStract2 = tempStract1;
            tempStract1.a = 2;
            tempStract1.b = 2;
            Console.WriteLine(tempStract1.a + " :" + tempStract1.b);
            Console.WriteLine(tempStract2.a + " :" + tempStract2.b);

4.結果如下:2:2 1:1 那就說明了:對於值類型而言會在棧上重新開闢一塊新的空間,將值複製過去。tempStract1和tempStract2是相互獨立的,在上述中改變了tempStract1的值並不會影響到tempStract2.

2 什麼是引用傳遞?

            var tempClass1 = new GameStateClass();
            tempClass1.a = 1;
            tempClass1.b = 1;
            var tempClass2 = tempClass1;
            tempClass1.a = 2;
            tempClass1.b = 2;
            Console.WriteLine(tempClass1.a + " :" + tempClass1.b);
            Console.WriteLine(tempClass2.a + " :" + tempClass2.b);

結果如下:2:2 2:2 那就說明了:對於引用類型而言也會在棧上重新開闢一塊新的空間,但注意這裏和值傳遞的不同的是:這裏會將棧上的引用複製到新開闢的空間,引用,引用,引用!爲什麼可以這麼說呢?因爲從打印結果來看我們只是改變了tempClass1的a和b的值,並沒有去改變tempClass2的a和b的值,但是結果卻是一樣的。說明了改變tempClass1的值會對tempClass2的值產生影響。如下圖所示:

在這裏插入圖片描述

從上圖就可以很好的解釋爲什麼。當我們執行完var tempClass1 = new GameStateClass();時候如圖中黑線所示,在棧中會開闢一個空間存tempClass1對 堆上new的GameStateClass對象的引用(也可以說爲指針)。然後當tempClass2 = tempClass1執行的時候會在棧中重新開闢一個空間存tempClass2然後將tempClass1的引用賦值給他,也就是說tempClass2存儲的也是指向堆的new的GameStateClass的對象。爲了形象化這裏的箭頭可以看做引用。最後當我們通過tempClass1去修改對象的數據之後,因爲tempClass1和tempClass2都是指向同一個對象,所以我們通過tempClass2去訪問對象的屬性a和b的時候,結果也爲一樣的。這就驗證了上面的打印結果爲什麼時2:2 2:2了。

綜上所述:值傳遞和引用傳遞的區別就是:值類型是複製數據本身,形成相互獨立的數據存儲區,引用類型是複製引用(指針),存儲的是引用,引用指向同一對象。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章