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;
    }
}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值