CS4L24——值类型和引用类型补充

知识回顾

在C#中,所有的变量类型都会分为值类型和引用类型,分类如下:

  • 值类型

    • 无符号:byte​,ushort​,uint​,ulong
    • 有符号:sbyte​,short​,int​,long
    • 浮点数:float​,double​,decimal
    • 特殊:char​,bool
    • 枚举:enum
    • 结构体:struct
  • 引用类型

    • string
    • 数组
    • interface
    • class
    • 委托

查看值类型还是引用类型

两者的本质区别,值类型在栈内存上,引用类型在堆内存上
按 F12 可以进一个类型的内部查看,是 class​ 就是引用类型,struct​ 就是值类型

语句块

1
2
3
4
5
6
7
8
9
10
命名空间
{
类、接口、结构体
{
函数、属性、索引器、运算符重载等(类、接口、结构体)
{
条件分支、循环等
}
}
}
  • 上层语句块:类、结构体
  • 中层语句块:函数
  • 底层语句块:条件分支,循环

我们的逻辑代码写在:函数、条件分支、循环-中底层语句块中

我们的变量在上、中、底都能申明,其中:

  • 上层语句块中:成员变量
  • 中、底层语句块中:临时变量

变量的生命周期

编程时大部分都是临时变量,在中底层申明的临时变量(函数、条件分支、循环语句块等)
语句块执行结束,没有被记录的对象将被回收或变成垃圾

  • 值类型:被系统自动回收
  • 引用类型:栈上用于存储地址的内存被系统自动回收,堆中具体内容变成垃圾,待下次 GC 回收

想要不被回收或者不变垃圾,必须将其记录下来,如何记录:在更高层级记录或者使用静态全局变量记录

比如底层的代码块里定义的非静态变量就不能给更高级的代码用

1
2
3
4
5
if (true)
{
int b = 1;
}
Console.WriteLine(b); // error: 当前上下文中不存在名称“b”

类似于这种代码,这种写法的 index​ 会反复被回收和加入,增加性能开销,同时每次循环的 index​ 都不会是同一个 index​:

1
2
3
4
while (true)
{
int index = 0;
}

因此这种定义变量一般写在外面,而且每次循环的 index​ 都是同一个 index

1
2
3
4
5
int index = 0;
while (true)
{
index = 1;
}

结构体中的值和引用

首先明确:值类型始终存储的都是数值,引用类型始终存储的都是地址,不管在堆里还是在栈里

结构体本身是值类型,前提是:该结构体没有作为其他类的成员

  • 在结构体中的值,栈中存储值具体的内容
  • 在结构体中的引用,栈存的是地址,堆中存储引用的具体内容

引用类型始终存储于堆中:真正通过结构体使用其中引用类型是只是顺藤摸瓜(顺着地址找到对应的堆内存)

类中的值和引用

类本身就是引用类型,在类中的值和引用,都是堆中存储值具体的内容
值类型在值类型里就存储在栈里,在引用类型里就在存储在堆里,引用类型都是存储在堆里

即使是分配了同一个连续房间(类,结构体),
值类型始终存储的都是数值,引用类型始终存储的都是地址(数据存储在堆里的另外一个房间内),不管在堆里还是在栈里

数组的存储规则

数组本身是引用类型

  • 值类型数组,堆中房间存储具体内容
  • 引用类型数组,堆中房间存储地址(数据存储在堆里的另外一个房间内)

结构体继承接口

利用里氏替换原则,用接口(引用类型)装载结构体(值类型)就会存在装箱拆箱!