引用类型和值类型的概念
面向.NET框架的所有语言的数据类型都由.NET框架中的通用系统类型CTS(Common Type System)定义。
• 值类型:存储数据本身
• 引用类型:存储数据的引用(内存地址)
划分
注意:C#的bool、char和所有数值类型都是由结构体定义的。
堆和栈
程序运行时,公共语言运行库 (common language runtime,CLR) 会向操作系统申请内存并进行划分区域,其中就有堆区和栈区。
- 栈(Stack):
- 空间小,读取速度快。
- 用于存储正在执行的方法,分配的空间叫做栈帧。栈帧中存储方法的参数以及变量等数据。方法执行完毕后,对应的栈帧将被清除。
- 堆(Heap):
- 空间大,读取速度慢。
- 用于存储引用类型的数据。
装箱和拆箱
当值类型的数据转换为引用类型时为装箱,反之为拆箱。
储存
局部变量
• 值类型:声明在栈中,保存在栈中
• 引用类型:声明在栈中,保存在堆中
成员变量
• 值类型:声明在堆中,保存在堆中
• 引用类型:声明在堆中,保存在堆中的另一块空间
分析
(对局部变量存储位置的分析)
值类型的使用特点
int s1 = 1;
int s2 = s1;//将s1的值赋给s2
s1 = 2;
Console.WriteLine (s2);// 1
这段代码中,因为int是值类型,数据直接保存在栈上,s1s2所在的地址直接保存值(1or2)。
s1 s2并无直接关联。
引用类型的使用特点
int[] arr1 = new int[] { 1, 2, 3 };
int[] arr2 = arr1;
arr1[0] = 6;
Console.WriteLine (arr2[0]);//6
声明为引用类型的字段在栈中的存放的是数据在堆中的地址。new
关键字就是在堆中申请一段内存然后返回此段内存的地址。
上例中,在栈中声明数组后在堆区申请了一段内存存放数组的实际数据,并将此数组在堆中的地址存储在声明(arr1)中。
由于arr1存放的是实际数组的地址(引用),将arr1赋值给arr2实际就是把arr1中存放的地址赋值给arr2在栈中的内存;赋值完成之后arr1和arr2指向的就是同一个数组。
由此通过arr1修改数组的值和通过arr2读取数据本质上都是在对同一个数组进行操作。可以认为arr1和arr2是一个数组的两个名字。
string
string s1 = "man";
string s2 = s1;
s1 = "woman";
Console.WriteLine (s2); //man
C#中,字符串具有不可变性。
下图是两个例子,我也很疑惑,暂时先记住使用规则吧。
由此留下两个疑问:
1. clr是怎么区分值类型和引用类型的?
2. 为什么可以直接把int赋值给object?int不全是由结构体定义的吗?(貌似所有的值类型都可以这么干)
等我以后查到了/学到了再回来填坑吧…
总结
- 方法执行在栈中;
- 所以在方法中声明的变量都在栈中;
- 因为值类型直接存储数据,所以数据存储在栈中;
- 又因为引用类型存储数据的引用,所以数据在堆中,栈中存储数据的引用。
核心就是:看改变的是栈上的数据还是堆上的数据