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 的目标 | 是 | 否 |
默认访问修饰符 | internal | public |
字段初始化 | 允许 | 不允许 |
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; // 拆箱
总结
选择使用类还是结构体应基于以下考虑:
- 类型的大小(结构体应保持小型)
- 所需语义(值语义 vs 引用语义)
- 性能需求
- 是否需要继承或多态
- 生命周期管理需求
理解这些区别有助于在C#开发中做出更合理的设计决策,编写出更高效、更可靠的代码。