首先明确类型:
常用的值类型有: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;
}
}