c#值类型和引用类型
一、程序结构
命名空间
↓
类、接口、结构体
↓
函数、属性、索引器、运算符重载等(类、接口、结构体)
↓
条件分支、循环
上层语句块:类、结构体
中层语句块:函数
底层的语句块: 条件分支 循环等
上层语句块中是成员变量;中、底层语句块中是临时变量。
二、变量的生命周期
在中底层申明的临时变量(函数、条件分支、循环语句块等)
语句块执行结束 ,没有被记录的对象将被回收或变成垃圾。
值类型:被系统自动回收
引用类型:栈上用于存地址的房间被系统自动回收,堆中具体内容变成垃圾,待下次GC回收
想要不被回收或者不变垃圾,必须将其记录下来。
在更高层级记录或者使用静态全局变量记录。
三、结构体中的值和引用
结构体本身是值类型
前提:该结构体没有做为其它类的成员
在结构体中的值,栈中存储值具体的内容;在结构体中的引用,堆中存储引用具体的内容。
引用类型始终存储在堆中
栈(Stack) 堆(Heap)
+----------------+ +------------------+
| p (PersonStruct)| | "张三" (string对象)|
| +------------+ | +------------------+
| | Id = 100 | | ^
| | Name = ref----+--------------------+
| | Scores = ref----+------------------+
| +------------+ | |
+----------------+ v
+------------------+
| [100,95,88] (数组)|
+------------------+
栈中存储的是值类型的数据和引用类型的地址 。这些地址指向了堆中对应位置 通过这些位置找到实际数据,堆上存的是引用类型的实际值。
四、类中的值和引用
类本身是引用类型。在类中的值,堆中存储具体的值,在类中的引用,堆中存储具体的值
栈 堆
┌──────┐ ┌─────────────────────────────────────────────┐
│ p │───▶│ Person对象 (堆) │
└──────┘ │ ┌─────┐ ┌──────┐ ┌────────┐ │
│ │ Age │ │ Name │ │ Scores │ │
│ │ 25 │ │ 引用 │ │ 引用 │ │
│ └─────┘ └──│───┘ └───│────┘ │
└─────────────┼─────────┼─────────────────────┘
│ │
▼ ▼
┌─────────────┴───┐ ┌─┴─────────────────┐
│ "张三" (string) │ │ int[] 数组 │
└─────────────────┘ │ ┌────┬────┬────┐ │
│ │ 90 │ 95 │ 88 │ │
│ └────┴────┴────┘ │
└───────────────────┘
五、数组中的存储规则
数组本身是引用类型 值类型数组,堆中房间存具体内容
引用类型数组,堆中房间存地址
六、结构体继承接口
能用接口的地方都能用结构体
interface ITest
{
int Value
{
get;
set;
}
}
struct TestStruct : ITest
{
int value;
public int Value
{
get
{
return value;
}
set
{
this.value = value;
}
}
}
TestStruct obj1 = new TestStruct();
obj1.Value = 1;
Console.WriteLine(obj1.Value);
TestStruct obj2 = obj1;
obj2.Value = 2;
Console.WriteLine(obj1.Value);
Console.WriteLine(obj2.Value);
ITest iObj1 = obj1;//装箱 value 1
ITest iObj2 = iObj1;
iObj2.Value = 99;
Console.WriteLine(iObj1.Value);
Console.WriteLine(iObj2.Value);
TestStruct obj3 = (TestStruct)iObj1;//拆箱
结构体本身值类型,变量独立,修改不受影响
ITest iObj1 = obj1;
由于 ITest 是引用类型,而 obj1 是值类型,这里发生装箱(Boxing):
在堆上分配一个新对象,将 obj1 的数据复制过去。iObj1 指向这个堆对象(不再是栈上的 obj1)。
ITest iObj2 = iObj1;
引用类型赋值,iObj2 和 iObj1 指向同一个装箱后的堆对象。
iObj2.Value = 99; 修改的是堆上的对象,因此通过 iObj1 和 iObj2 访问到的都是 99。
输出:99、99。
七、详细分析
值类型
-
a是一个局部变量,类型是int(值类型)。
在 .NET / C# 中,局部值类型变量通常分配在栈(Stack)上。所以a占据的 4 个字节在栈上。 -
1是一个字面量(常量),它不会被单独分配一块内存。
编译器将1直接编码到 IL 指令中(如ldc.i4.1),运行时作为立即数使用,不需要额外的存储位置。
当执行int a = 1;时,CPU 直接将数值1写入a所在的栈位置。 -
从逻辑上,你可以理解为“
a在栈上,它的值是1(也在栈上)”,但这并不是说有一个独立的1对象在栈上;而是说a所在的那块栈内存中存放的数值就是1。
更多推荐



所有评论(0)