C#学习日记

发布于:2025-07-15 ⋅ 阅读:(16) ⋅ 点赞:(0)

一、基础概念回顾:

值类型变量直接包含值本身,通常分配在栈(Stack)内存中

  • 基本数据类型:int, float, char, bool, enum

  • 自定义结构体 struct

引用类型(Reference Type)

引用类型变量包含的是指向实际对象的引用地址,实际数据位于堆(Heap)内存中

  • string(虽然看起来像值,但本质是引用类型)

  • 数组、类 class

  • 接口 interface、委托 delegate

📌 结构体(struct)是值类型,类(class)是引用类型。
可以参考下面这篇博客
秒懂 栈内存和堆内存(深入底层)-CSDN博客

二、如何判断类型是值类型还是引用类型

int i = 12;         // struct,值类型
string str = "123"; // class,引用类型(不可变)

快捷判断方法:

  • F12 查看类型定义,是 struct 就是值类型,是 class 就是引用类型。

    内存类型 存储的常见数据类型 / 数据 特点说明
    栈内存 基本数据类型(如 int、float、boolean、char 等) 占用空间小、大小固定,由系统自动分配和释放,访问速度快
    栈内存 引用数据类型的引用(如对象的地址、数组的地址) 存储的是指向堆内存中对象的地址,而非对象本身,随作用域结束自动释放
    堆内存 引用数据类型的实例(如对象、数组、字符串常量池以外的字符串) 占用空间可动态变化,由程序员通过代码(如 new)申请,一般需手动释放(或由垃圾回收机制处理),访问速度相对较慢

  • 注:字符串的存储较为特殊,字符串常量通常存放在字符串常量池中(属于方法区,非堆内存,但有些实现中会将其归为堆的一部分),而通过new创建的字符串对象则存放在堆内存中。

三、变量生命周期

临时变量

int i2 = 1;
string str2 = "123";

定义在方法、if、while等语句块中,生命周期只在语句块内。

  • 值类型:变量消失,值直接被系统自动销毁

  • 引用类型:引用被销毁,堆上的对象不会立刻销毁,但会变成垃圾对象,等待 GC 回收

int c = 10;
Test.TestI = c; // 静态变量,全局存在,不会自动销毁

想保留变量生命周期?

  • 定义为类字段/静态字段

  • 或存在更高层作用域中

C语言-局部变量、全局变量、静态变量和生命周期_c语言全局变量局部变量静态变量-CSDN博客



 

 四、结构体中值和引用
 

TestStrict ts = new TestStrict();
  • TestStrict 是一个结构体(值类型),其内部含有:

    • Test t:引用类型成员,指向堆上数据

    • int i:值类型成员,值存在栈上

结构体中引用类型的成员依旧存堆上,只是结构体本身在栈中,保存着引用的地址。
C语言中结构体 自引用 和 相互引用_c语言结构体指针自引用-CSDN博客

五、类中值和引用

Test t = new Test();

  • Test 是类,属于引用类型,变量 t 指向堆内存。

  • 类中的字段 bstrts 都存储在堆内存中。

  • 引用类型的值类型成员也会随对象一同存储在堆中

 六、数组的内存存储规则

int[] arrayInt = new int[5];       // 值类型数组
object[] objs = new object[5];     // 引用类型数组
数组类型 内容存储位置
int[] 每个元素都直接存堆中的具体值
object[] 每个元素存的是引用,指向其他堆上对象

 七、结构体实现接口:装箱与拆箱

ITest iObj1 = obj1;      // 装箱
iObj2.Value = 99;
TestStruct obj3 = (TestStruct)iObj1;  // 拆箱

装箱(Boxing):

  • 值类型包装成引用类型(如接口、object);

  • 会在堆上新建一个副本,不会影响原始值。

拆箱(Unboxing):

  • 从引用类型中提取出原始的值类型对象。

关键点:结构体装箱会产生性能开销,赋值后修改不会影响原值

输出分析:

obj1.Value = 1;
obj2.Value = 2;
Console.WriteLine(obj1.Value); // 1(说明 obj2 是 obj1 的拷贝)

iObj1 = obj1;
iObj2 = iObj1;
iObj2.Value = 99;
Console.WriteLine(iObj1.Value); // 1(修改无效,是装箱后的副本)

八、GC(垃圾回收) 在这段代码中的体现
 

虽然你没有显式调用 GC.Collect(),但这段代码隐含地体现了 GC 的触发场景

  1. 当结构体中的引用成员失效(如 Test 类对象没被引用);

  2. 当函数结束,临时变量的栈空间释放;

  3. 此时引用类型实例变为“孤岛”,等待 GC。

回顾GC的触发逻辑:
 

  • 引用类型对象失去所有引用后,进入垃圾状态

  • GC 会在内存压力大时自动触发(或手动调用 GC.Collect())。




 


网站公告

今日签到

点亮在社区的每一天
去签到