Unity基础系列:C#的值类型和引用类型
大家好呀!我是稿费,想了很久准备回归写一写博客,希望自己的理解能帮助到有需要的人(当然有些理解可能是有偏差的,毕竟还是菜鸟一个嘛,希望大家能狠狠指出来鞭策我!!!)
如果需要转载使用则需要附上作者跟原文链接!毕竟打字超累的好不好!
C#的值类型和引用类型的区别
首先抛开什么堆跟栈一大堆的东西,值类型(例如int,float之类的基础类型都属于值类型(string是引用类型,但是每次赋值新变量都是拷贝,行为像值类型),自定义的值类型就enum和struct)和引用类型的区别就是:值类型的使用是拷贝,引用类型的使用是引用。(好像没说一样)
俗话说得好,实践出真章,让我们直接看下代码理解下上面那句话
public class Test : MonoBehaviour
{
public struct MyStruct {
public int structInt;
}
public class MyClass {
public int classInt;
}
// Start is called before the first frame update
void Start() {
// Struct
MyStruct myStruct1 = new MyStruct();
myStruct1.structInt = 1;
MyStruct myStruct2 = myStruct1;
myStruct2.structInt = 2;
Debug.Log($"Stuct1 value is : {myStruct1.structInt}");
Debug.Log($"Stuct2 value is : {myStruct2.structInt}");
// Class
MyClass myClass1 = new MyClass();
myClass1.classInt = 1;
MyClass myClass2 = myClass1;
myClass2.classInt = 2;
Debug.Log($"Class1 value is : {myClass1.classInt}");
Debug.Log($"Class2 value is : {myClass2.classInt}");
}
}
上面就是我们的测试代码啦!我们先不看结果大家可以猜猜答案是什么!
1.
2.
3.
公布答案:
很神奇是嘛!Class1的改变居然影响了Class2!
现在让我们通过我们上面所说的来理解下为什么会产生这样的结果:
首先是Struct
- struct1通过copy将自己本身复制一份一模一样的给了struct2(相当于struct1告诉struct2:卷子我复印一份给你吧!)
- struct2拿到copy后,相当于一份独立的副本(struct2说:行吧!那我就拿走了)
- struct1的修改已经独立于struct2,互不影响。(struct1拿回家后发现有需要修改的地方进行修改,但是struct2手上的卷子却还是之前的版本)
值类型的拷贝就可以用上面的例子来理解
接下来我们看下Class到底做了什么吧
- class1则是将自己持有的class1的引用交给了class2。(类比上面的struct1和struct2,只是这次class1将装卷子的文件袋跟class2共享了,而不是复印一份)
- class1和class2操作的变量其实是同一个东西(因为从始至终卷子都没被拷贝,就算class1修改了卷子,class2打开书包看到的也是同一个修改后的卷子)
相信通过上面的例子大家已经理解了吧~
现在让我们想想他们分配的地方:堆和栈
- 对于引用类型想都不用想,一定分配在托管堆内!(没错就是这么霸道)
- 对于值类型则是看情况,如果在函数体内用完就丢弃(不出现闭包的情况)则在栈上分配。如果是在class内部声明的值类型,那么sorry啊,就是跟随class在堆上分配。
- 这个是重点,那天被问到了一个问题,我整个人都愣住了:如果值类型内部含有一个引用类型,那他是如何分配内存,会在栈上还是堆上!其实弄清楚这个概念很重要的一点是无论是引用类型还是值类型,他们得字段其实存的都是一个变量的内存位置。所以就算值类型里面含有引用类型,其实可以当做特殊的int处理。只是值类型内部class指向的东西是在堆里面。通过上面的分析大家也可以知道,就算值类型内部含有引用字段也是当做普通的值类型处理。(不好理解吧,直接上代码!!!!)
public class Test : MonoBehaviour
{
public struct MyStruct {
public int structInt;
public MyClass structClass;
}
public class MyClass {
public int classInt;
}
// Start is called before the first frame update
void Start() {
// Struct
MyStruct myStruct1 = new MyStruct();
myStruct1.structInt = 1;
myStruct1.structClass = new MyClass();
myStruct1.structClass.classInt = 1;
MyStruct myStruct2 = myStruct1;
myStruct2.structInt = 2;
myStruct1.structClass.classInt = 2;
Debug.Log($"Stuct1 value is : {myStruct1.structInt}");
Debug.Log($"Stuct2 value is : {myStruct2.structInt}");
Debug.Log($"Stuct1Class value is : {myStruct1.structClass.classInt}");
Debug.Log($"Stuct2Class value is : {myStruct2.structClass.classInt}");
}
}
输出:
这些就是全部啦,相信通过这个文章一定让你对值类型和引用类型有更多的理解吧!