【20260820 学习笔记】值类型与引用类型 using System; namespace _2_StudyValueTypeAndReferenceType { // 1.值类型struct 结构体 /// summary /// struct 结构体值类型数据存在栈内存 /// 赋值的时候完整拷贝一份数据两个变量互相独立互不干扰 /// /summary struct PointStruct { public int X; public int Y; public PointStruct(int x, int y) { X x; Y y; } public void ShowInfo() { Console.WriteLine($结构体PointX{X}, Y{Y}); } } // 2.引用类型class 类 /// summary /// class 类引用类型对象数据存堆内存变量存的是地址(引用) /// 赋值的时候拷贝地址多个变量指向同一个堆里面的对象改一个全部跟着变 /// /summary class PointClass { public int X; public int Y; public PointClass(int x, int y) { X x; Y y; } public void ShowInfo() { Console.WriteLine($类PointX{X}, Y{Y}); } } class Program { static void Main(string[] args) { Console.WriteLine(【第一部分值类型 struct 赋值演示】); // 值类型 struct PointStruct s1 new PointStruct(10, 20); s1.ShowInfo(); // ⭐值类型赋值把s1里面的数据完整复制一份给 s2两者完全独立 PointStruct s2 s1; Console.WriteLine(执行 PointStruct s2 s1; 之后修改s2的值); s2.X 999; // 修改s2不会影响s1 s1.ShowInfo(); s2.ShowInfo(); Console.WriteLine(); Console.WriteLine(【第二部分引用类型 class 赋值演示】); // 引用类型 class new出来对象放堆上变量存地址 PointClass c1 new PointClass(10, 20); c1.ShowInfo(); // ⭐引用类型赋值只复制地址c1、c2指向堆中同一个对象 PointClass c2 c1; Console.WriteLine(执行 PointClass c2 c1; 之后修改c2.X); c2.X 999; // 修改c2c1也跟着变同一个对象 c1.ShowInfo(); c2.ShowInfo(); Console.WriteLine(); Console.WriteLine(【第三部分作为方法参数传递】); Console.WriteLine(---3.1 值类型作为参数传的是拷贝方法内部修改不影响外面变量---); PointStruct st new PointStruct(1, 2); ChangeStruct(st); Console.WriteLine(方法调用结束后外面st的值); st.ShowInfo(); Console.WriteLine(\n---3.2 引用类型作为参数传地址方法内部修改会改变外面对象的数据---); PointClass cl new PointClass(1, 2); ChangeClass(cl); Console.WriteLine(方法调用结束后外面cl的值); cl.ShowInfo(); Console.WriteLine(); Console.WriteLine(【第四部分null 引用类型专属】); // ❌值类型不能赋值null // PointStruct sNull null; //编译报错struct是值类型不能为空 // ✅引用类型可以赋值null表示这个变量不指向堆中任何对象 PointClass cNull null; Console.WriteLine($cNull是否为null{cNull null}); // cNull.ShowInfo(); //运行报错空引用异常 NullReferenceException Console.WriteLine(); Console.WriteLine(【第五部分常见值类型、引用类型清单】); Console.WriteLine(值类型struct结构体、int double bool char DateTime、枚举enum存栈); Console.WriteLine(引用类型class类、string字符串、数组、委托、接口存堆); Console.WriteLine(\n⚠️小坑string是引用类型但行为表现像值类型字符串不可变); string strA 你好; string strB strA; strB 世界; Console.WriteLine($strA{strA}, strB{strB}); Console.ReadKey(); } /// summary /// 值类型参数传入的是副本 /// /summary static void ChangeStruct(PointStruct p) { p.X 888; Console.WriteLine(方法内部修改后p的值); p.ShowInfo(); } /// summary /// 引用类型参数传入对象的地址 /// /summary static void ChangeClass(PointClass p) { p.X 888; Console.WriteLine(方法内部修改后p的值); p.ShowInfo(); } } }值类型与引用类型知识点总结 基于上面示例代码整理逐条记录一、struct 结构体值类型存储在栈内存struct 是结构体属于值类型数据存储在栈内存。结构体可以写构造函数给字段初始化可以拥有字段、方法。赋值行为PointStruct s2 s1;会完整拷贝一份全部数据副本。赋值后两个变量完全独立修改其中一个变量的字段不会影响另一个变量。不允许赋值 null。PointStruct s null;直接编译报错值类型天生一定有有效值。作为方法参数传递默认传递的是数据副本。方法内部对参数字段做修改仅仅修改副本不会影响外部原始变量。内存特点变量本身就存储完整的字段数据不需要 new 堆对象。二、class 普通类引用类型存储在堆内存class 普通类属于引用类型。new 类名()会在堆内存创建对象保存真实字段数据栈上的变量只保存堆对象的内存地址引用。赋值行为PointClass c2 c1;只拷贝栈上的地址不会复制堆上对象。多个变量保存同一个地址指向堆中同一个对象任意一个变量修改对象字段所有变量读取到的数据都会跟着改变。可以赋值 null。PointClass cNull null;代表该引用变量不指向堆里任何对象。空引用异常变量为 null 时调用对象的字段、方法运行时报NullReferenceException。作为方法参数传递传递地址副本仍然指向堆同一个对象。方法内部修改对象的字段外部原始变量可以看到修改后的结果。注意如果方法内部执行p new PointClass(xxx)只是修改局部参数 p 的指向不会改变外面的变量。三、string 字符串特殊引用类型string 是引用类型但是表现行为很像值类型核心原因是字符串不可变。字符串对象一旦在堆创建完成内容不能被修改。strB strA;两个变量指向同一个字符串对象。当执行strB 世界;不是修改原有堆字符串而是在堆新建字符串对象让 strB 指向新地址。结果修改 strBstrA 的值不受影响看起来和值类型效果一致。四、值类型、引用类型常见清单值类型struct 自定义结构体、int、double、bool、char、DateTime、enum 枚举数据主要存栈。引用类型class 类、string 字符串、数组、委托、接口对象真实数据存堆栈存地址。五、ref 关键字拓展改变值类型传参行为值类型默认传副本方法参数加上ref传递变量本身的内存地址。方法内部修改参数字段外部原始值类型变量会被修改。调用时也必须带上 refChangeStructRef(ref st);六、Main 入口方法中的演示Main 是 static 静态方法。使用值类型new struct栈上生成数据传入方法默认拷贝副本。使用引用类型new class堆生成对象变量存地址传给方法共享堆对象。可以把引用类型变量赋值为 null模拟空引用场景。七、核心记忆对照表类型类别内存存储赋值行为参数默认传递能否为 nullstruct 结构体值类型栈存完整数据拷贝全部数据两份独立传副本内部修改不影响外部❌不可以class 类引用类型栈存地址堆存对象拷贝地址指向同一个堆对象传地址副本修改字段影响外部✅可以string引用类型栈存地址堆存字符串对象字符串不可变赋值会产生新对象传地址副本✅可以八、高频编译 运行错误点总结值类型 struct 写变量 null;→编译报错值类型不能为空。引用类型变量赋值 null调用null对象.方法()→运行报错 NullReferenceException 空引用异常。值类型方法传参方法内部修改参数外部变量没有变化属于正常现象不是 bug。string 是引用类型不要误以为它就是值类型只是因为字符串不可变表现类似值类型。九、通俗口诀值类型存栈赋值拷贝全数据你改你的我不变引用类型栈存地址堆里放对象赋值拷贝地址一改大家变。struct 不能赋 nullclass 可以空string 虽是引用类内容一旦创建不能改赋值就换新对象。值类型传参默认传副本想要改外面ref 关键字来帮忙。