C# struct 结构体 vs class 类|内存底层完整对比(上位机开发必读)

目录
前置认知:栈内存 & 堆内存
struct 结构体 VS class 类 全方位对比表
通俗类比,一秒理解复制机制
实战对照代码
版本 1:class 类(引用类型演示)
版本 2:struct 结构体(值类型演示)
结构体高频易错坑汇总
上位机开发如何选择?实战场景划分
一、前置认知:栈内存 & 堆内存
栈内存 Stack
空间偏小、读写速度极快;存放值类型(int、double、struct)。方法执行结束自动释放内存,无需垃圾回收。
堆内存 Heap
空间充足;存放引用类型(class、数组)。真实数据存在堆中,栈只保存地址,依靠 GC 垃圾回收清理。
二、struct 结构体 VS class 类 全方位对比表
表格
对比维度 struct 结构体(值类型) class 类(引用类型)
内存存储 完整数据直接保存在栈内存 真实数据存放堆内存;栈仅保存堆的地址编号
赋值行为 B = A 完整拷贝 A 所有数据,生成独立副本,两块互不干扰内存 仅复制堆地址;两个变量指向堆内同一份原始数据
修改联动效果 修改 A,B 完全不受影响,相互独立 修改 A 内部字段,B 同步跟着变化
内存开销 数据直接存储,无额外地址开销 额外保存地址信息,内存开销更大
适用数据规模 轻量小型数据包(传感器参数、坐标、临时采集数据) 复杂对象、需要多地方共享的数据(设备实例、窗口、报警记录)
内存释放 方法运行结束,栈内存自动释放 等待 GC 垃圾回收机制清理堆内存
三、生活化类比(强烈建议背诵)
struct 结构体(值拷贝)
你有一张纸质表格book1,复印机复印出一张全新表格book2。
你在原件上手写修改内容,复印件不会产生任何变化,两份表格互相独立。
class 类(地址引用)
柜子里只存放唯一一张原始表格(堆内存)。
你和朋友各自拥有一张纸条,纸条上面写着柜子编号(内存地址)。
任何人打开柜子修改表格内容,所有人看到的数据都会同步更新。
四、对照实战代码演示
示例 1:class 类(引用类型)
赋值仅复制地址,多个变量共用同一份数据
csharp
运行
using System;

namespace ClassDemo
{
// 定义引用类型:类
class Book
{
// 公开字段
public string name;
public double price;
public string desc;
public int count;
}

internal class Program
{
static void Main(string[] args)
{
// 在堆内存创建对象,栈中保存地址
Book book1 = new Book()
{
name = "西游记",
price = 9.9,
count = 100,
desc = "猴子打妖怪"
};

// ⚠重点:仅仅复制内存地址,book1、book2指向堆中同一个对象
Book book2 = book1;

// 修改堆内原始数据
book1.name = "水浒传";

// book2 和 book1共用一份数据,输出:水浒传
Console.WriteLine("book2的书名:" + book2.name);

Console.WriteLine($"\nbook1信息:{book1.name},价格{book1.price},销量{book1.count},简介:{book1.desc}");
Console.WriteLine($"book2信息:{book2.name},价格{book2.price},销量{book2.count},简介:{book2.desc}");

Console.WriteLine("\n按任意键关闭窗口");
Console.ReadKey();
}
}
}
运行结果:
plaintext
book2的书名:水浒传

book1信息:水浒传,价格9.9,销量100,简介:猴子打妖怪
book2信息:水浒传,价格9.9,销量100,简介:猴子打妖怪
示例 2:struct 结构体(值类型)
赋值执行完整数据拷贝,两份数据互相独立
csharp
运行
using System;

namespace StructDemo
{
internal class Program
{
// 结构体定义位置:类内部、Main方法外部
struct Book
{
// 公开字段,存储书籍信息
public string name; //书名
public double price; //书籍价格
public string desc; //书籍简介
public int count; //售卖数量
}

static void Main(string[] args)
{
// 在栈内存开辟空间,保存完整数据
Book book1 = new Book()
{
name = "西游记",
price = 9.9,
count = 100,
desc = "猴子打妖怪"
};

// ⚠重点:值类型完整拷贝!栈中新开辟内存,复制全部数据
Book book2 = book1;

// 仅修改book1栈空间的数据,不会影响book2
book1.name = "水浒传";
book1.price = 99.9;
book1.count = 200;
book1.desc = "梁山好汉";

Console.WriteLine($"book1的名字是:{book1.name},售价是:{book1.price},卖了{book1.count}本,讲了关于{book1.desc}的故事");
Console.WriteLine($"book2的名字是:{book2.name},售价是:{book2.price},卖了{book2.count}本,讲了关于{book2.desc}的故事");

Console.ReadKey();
}
}
}
运行结果:
plaintext
book1的名字是:水浒传,售价是:99.9,卖了200本,讲了关于梁山好汉的故事
book2的名字是:西游记,售价是:9.9,卖了100本,讲了关于猴子打妖怪的故事
五、结构体高频易错点汇总
定义位置限制
struct 不能写在 Main 方法内部!只能放在类内、方法外部,否则直接编译报错(和 enum 枚举规则一致)。
拷贝一次性生效
Book book2 = book1; 只会复制赋值瞬间的所有数据。复制完成后两者彻底隔离,后续修改互不干扰。
开发规范补充
示例使用public公开字段是为了新手学习;正式工程项目推荐使用私有字段 + 属性封装,规范数据访问。
不要无脑滥用 struct
结构体拷贝时会复制全部数据,如果内部存放大量数据,频繁复制会带来性能损耗。
六、上位机开发实战选型指南(重点!贴合你的学习方向)
✅ 优先使用 struct 场景
传感器采集数据包:温度、压力、转速临时采集数据;复制缓存数据时,不会污染原始采集值
运动控制点位:机械轴 X/Y/Z 坐标、视觉标定点位这类轻量多参数组合
串口通讯临时缓存:Modbus 寄存器临时数值、校验参数,数据独立、不需要全局共享
✅ 优先使用 class 场景
全局设备对象、设备运行状态
窗口实例、实时报警列表
多界面、多线程需要同步共享同一组数据
核心口诀:数据需要独立副本 → struct;多处代码共用一份数据 → class
七、文末总结
struct = 自定义值类型,栈存储,赋值完整复制,数据相互独立;
class = 引用类型,堆存储,赋值只复制地址,多变量共享同一份数据;
上位机轻量数据包首选 struct;需要全局共享状态的业务对象优先 class。

posted @ 2026-07-29 21:16  狂浪的狼  阅读(2)  评论(0)    收藏  举报