当前位置: 首页 > ops >正文

C#基础篇(09)结构体(struct)与类(class)的详细区别

C# 中结构体(struct)与类(class)的详细区别

在 C# 中,结构体(struct)和类(class)都是用于创建自定义类型的构造,但它们有显著的区别,这些区别影响着它们的使用场景和性能表现。

1. 基本类型分类

  • 类(class)​​:引用类型(reference type)
  • 结构体(struct)​​:值类型(value type)

这是最根本的区别,由此衍生出许多其他差异。

2. 内存分配

  • ​:

    • 分配在堆(heap)上
    • 通过引用来访问
    • 由垃圾回收器(GC)管理内存
  • 结构体​:

    • 分配在栈(stack)上(通常情况)
    • 直接存储值本身
    • 超出作用域时立即释放
// 类示例
class MyClass { public int X; }// 结构体示例
struct MyStruct { public int X; }MyClass c = new MyClass(); // 在堆上分配
MyStruct s = new MyStruct(); // 在栈上分配(通常情况)

3. 默认值

  • ​:默认值为 null
  • 结构体​:不能为 null(除非使用可空类型),总是有默认值(各字段为默认值)
MyClass c; // c 为 null
MyStruct s; // s 已初始化,s.X == 0

4. 赋值行为

  • ​:赋值的是引用(两个变量指向同一对象)
  • 结构体​:赋值的是值的副本(创建新副本)
MyClass c1 = new MyClass { X = 1 };
MyClass c2 = c1; // c2 和 c1 指向同一对象
c2.X = 2; // c1.X 也变为 2MyStruct s1 = new MyStruct { X = 1 };
MyStruct s2 = s1; // 创建 s1 的副本
s2.X = 2; // s1.X 仍为 1

5. 继承

  • ​:

    • 支持单继承
    • 可以继承自另一个类
    • 可以实现多个接口
  • 结构体​:

    • 隐式继承自 System.ValueType
    • 不能继承自其他结构体或类
    • 可以实现接口
class BaseClass {}
class DerivedClass : BaseClass {} // 合法struct BaseStruct {}
// struct DerivedStruct : BaseStruct {} // 编译错误

6. 构造函数

  • ​:

    • 可以有无参构造函数
    • 如果没有定义构造函数,编译器会提供默认无参构造函数
  • 结构体​:

    • 总是有一个隐式的无参构造函数(即使定义了其他构造函数)
    • 不能自定义无参构造函数
    • 构造函数必须初始化所有字段
struct MyStruct
{public int X;public int Y;// 结构体构造函数必须初始化所有字段public MyStruct(int x, int y){X = x;Y = y;}// 不能定义无参构造函数// public MyStruct() { } // 编译错误
}

7. 性能考虑

  • ​:

    • 堆分配和垃圾回收可能带来性能开销
    • 适合大型对象或需要长期存在的对象
  • 结构体​:

    • 栈分配通常更快
    • 没有垃圾回收开销
    • 适合小型、短暂使用的对象
    • 频繁装箱/拆箱会降低性能

8. 适用场景

  • 使用类的情况​:

    • 需要继承
    • 对象标识重要(两个变量引用同一对象)
    • 对象较大
    • 对象生命周期较长
    • 需要多态行为
  • 使用结构体的情况​:

    • 表示简单的值类型(如坐标、颜色等)
    • 小型数据结构(通常小于16字节)
    • 不可变类型
    • 频繁创建和销毁的临时对象
    • 不需要多态行为

9. 其他区别

特性类(class)结构体(struct)
可以为 null否(除非可空类型)
可以包含析构函数
可以作为 lock 的目标
默认访问修饰符internalpublic
字段初始化允许不允许

10. 示例对比

// 类示例
public class PointClass
{public int X;public int Y;public PointClass(int x, int y){X = x;Y = y;}
}// 结构体示例
public struct PointStruct
{public int X;public int Y;public PointStruct(int x, int y){X = x;Y = y;}
}// 使用对比
PointClass pc1 = new PointClass(1, 2);
PointClass pc2 = pc1; // 复制引用
pc2.X = 3; // pc1.X 也变为 3PointStruct ps1 = new PointStruct(1, 2);
PointStruct ps2 = ps1; // 复制值
ps2.X = 3; // ps1.X 仍为 1

11. 可变性与不可变性

结构体通常更适合设计为不可变类型:

public readonly struct ImmutablePoint
{public readonly int X;public readonly int Y;public ImmutablePoint(int x, int y){X = x;Y = y;}
}

12. 装箱与拆箱

  • 结构体转换为 object 或接口类型时会发生装箱
  • 从 object 或接口类型转换回结构体会发生拆箱
  • 装箱/拆箱有性能开销,应尽量避免
MyStruct s = new MyStruct();
object boxed = s; // 装箱
MyStruct unboxed = (MyStruct)boxed; // 拆箱

总结

选择使用类还是结构体应基于以下考虑:

  1. 类型的大小(结构体应保持小型)
  2. 所需语义(值语义 vs 引用语义)
  3. 性能需求
  4. 是否需要继承或多态
  5. 生命周期管理需求

理解这些区别有助于在C#开发中做出更合理的设计决策,编写出更高效、更可靠的代码。

http://www.xdnf.cn/news/15008.html

相关文章:

  • 安卓设备信息查看器 - 源码编译
  • PiscTrace深蹲计数功能实现:基于 YOLO-Pose 和人体关键点分析
  • Unity Demo-3DFarm详解-其二
  • 【03】MFC入门到精通——MFC 添加控件 设置属性 按钮 文本框
  • EFK9.0.3 windows搭建
  • 供应链管理:定量分析中的无量纲化处理
  • jenkins+固定agent节点+maven+sonarqube+docker
  • Redis:分组与设备在 Redis 中缓存存储设计
  • 类与对象【下篇】-- 关于类的其它语法
  • Linux——I/O复用
  • Android API Level 到底是什么?和安卓什么关系?应用发布如何知道自己的版本?优雅草卓伊凡
  • 光伏无人机3D建模:毫秒级精度设计
  • Webpack、Vite配置技巧与CI/CD流程搭建全解析
  • 什么是公链?
  • 敏捷模式下开发与测试的最佳协作模式
  • Node.Js是什么?
  • 【bug修复积累】关于包装类型和基本数据类型的使用
  • 分库分表之实战-sharding-JDBC水平分库+分表后:查询与删除操作实战
  • SQLZoo 练习与测试答案汇总(复杂题有最优解与其他解法分析、解题技巧)
  • 全志H616开发学习文档
  • WPF学习笔记(27)科学计算器
  • ObjectClear - 图像处理新革命,一键“抹除”图像中任意物体与阴影 支持50系显卡 一键整合包下载
  • [附源码+数据库+毕业论]基于Spring Boot+mysql+vue结合内容推荐算法的学生咨询系统
  • 如何将FPGA设计的验证效率提升1000倍以上(3)
  • rt thread studio 和 KEIL对于使用rt thread 的中间件和组件,哪个更方便
  • 使用 Docker 搭建 Go Web 应用开发环境——AI教你学Docker
  • Mac mini 高性价比扩容 + Crossover 游戏实测 全流程手册
  • Go语言Gin框架实战:开发技巧
  • 【保姆级喂饭教程】Windows下安装Git Flow
  • LabVIEW前面板颜色修改