天天看點

C# 值傳遞和引用傳遞的了解

首先明确類型:

常用的值類型有:int double char bool decimal struct enum;

常用的引用類型有:string 數組 自定義類 接口 委托;

值類型的對象直接存在于棧上。

引用類型的對象實際存在于堆上,但是“指針”存在于棧上。

   例如 myClass A = new myClass();

  A應該存在于棧上,其内容是一個位址,指向的是new myClass的堆中位址。A應該就是一個代理人,通過A可以修改找到真實的對  象。

然後是傳遞方式:

對于兩種類型的傳遞,在非out,ref的情況下都是值傳遞【拷貝】,隻不過引用類型傳遞的是一個位址的拷貝,在函數内對該拷貝的修改實際上是對該拷貝指向的實際内容的修改。值類型是傳遞的是具體内容的拷貝。

1,按值傳遞

值類型按值傳遞,引用類型按值傳遞的實質的是傳遞值,參數為值類型時,“值”為執行個體本身,是以傳遞的是執行個體拷貝,不會對原來的執行個體産生影響;參數為引用類型時,“值”為對象引用,是以傳遞的是引用位址拷貝,會改變原來對象的引用指向。

string是引用類型,string按值傳遞的效果與值類型按值傳遞效果一樣,string在這裡比較特殊。

調用方法發生參數傳遞時,方法根據參數類型先在stack建立一個變量,然後将參數的值指派給該變量。是以,值類型與string類型傳遞執行個體不變,引用類型傳遞位址改變。但如果是按引用傳遞,則都是傳遞位址,執行個體的值都會發生改變。

2,按引用傳遞

按引用傳遞之ref和out,不管是值類型還是引用類型,按引用傳遞必須以ref或者out關鍵字來修飾,ref要求傳遞之前的參數必須首先顯示初始化,而out不需要。也就是說,使用ref的參數必須是一個實際的對象,而不能指向null;而使用out的參數可以接受指向null的對象,然後在調用方法内部必須完成對象的實體化。

值類型按引用傳遞時,不會對值類型裝箱。

按引用傳遞,傳遞的不是參數本身的值,而是參數的位址。如果參數為值類型,則傳遞的是該值類型的位址;如果參數為引用類型,則傳遞的是對象引用的位址,引用類型按引用傳遞結果和按值按引用傳遞一樣。

示範代碼:

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

public struct StructA

{

    public int a;

}

public class ChuandiA

{

    public ChuandiA(int _a)

    {

        a = _a;

    }

    private int a;

    public int A { get { return a; } set { a = value; } }

}

public class ChuandiTest : MonoBehaviour

{

    // Use this for initialization

    void Start()

    {

        Debug.Log("=====修改類的成員====");

        ChuandiA A = new ChuandiA(0); //對象1

        Debug.Log(A.A);

        Debug.Log("=======================");

        ChangeA(A);

        Debug.Log(A.A);

        Debug.Log("=====Ref修改類的成員====");

        ChuandiA B = A; //B拷貝了A的内容,B和A都指向了對象1。

        Debug.Log(A.A);

        Debug.Log(B.A);

        Debug.Log("=======================");

        ChangeA(ref A);

        Debug.Log(A.A); //B仍然指向對象1,A已經指向了新的對象。

        Debug.Log(B.A); //B仍然

        Debug.Log("=====修改結構體的成員===");

        StructA sA = new StructA();

        StructA sA2 = sA;

        sA.a = 33;       

        Debug.Log(sA.a);

        ChangeA2(sA);

        Debug.Log("=======================");

        Debug.Log(sA.a);

        Debug.Log("=======使用ref修改結構體的成員========");

        sA.a = 44;

        Debug.Log(sA.a);

        ChangeA2(ref sA);

        Debug.Log("=======================");

        Debug.Log(sA.a);

    }

    public void ChangeA(ChuandiA _ChuandiA)

    {

        //此時的_ChuandiA是傳入參數的拷貝,由于傳入的參數是引用類型,是以_ChuandiA的内容是一個位址,指向傳入參數。

        //首先通過位址修改了【傳入對象】的内容;

        _ChuandiA.A = 3;

        //然後指向了新的位址,并不會影響傳入對象的内容;

        _ChuandiA = new ChuandiA(99);

        //此時的_ChuandiA 指向新的ChuandiA,對舊的對象[sA]無影響。

    }

    public void ChangeA(ref ChuandiA _ChuandiA)

    {

        //_ChuandiA就是傳入參數,原來傳入參數是一個指針,指向真實的引用類型對象。

        _ChuandiA.A = 444;

        //修改了_ChuandiA的指向,也就是修改了傳入參數的内容【位址】.

        _ChuandiA = new ChuandiA(99);

        //傳入參數原來指向的對象仍然是444,不受影響。

    }

    public void ChangeA2(StructA _ChuandiA)

    {

        //值類型的值傳遞是拷貝。此時的_ChuandiA是對傳入參數的一個拷貝。

        _ChuandiA.a = 3;

    }

    public void ChangeA2(ref StructA _ChuandiA)

    {

        //值類型的引用傳遞不是拷貝,此時的_ChuandiA就是傳入參數。

        _ChuandiA.a = 3;

    }

}