C#中struct跟class的异同之处 - 实践
好的,这是 C# 中 struct
(结构)和 class
(类)的核心异同之处。理解它们的区别对于编写高效、正确的 C# 代码至关重要。
核心相同点
都是数据类型:都可以包含材料成员(字段、属性)和行为成员(方法、事件、索引器等)。
都支持接口:都可以实现一个或多个接口。
核心不同点(非常重要)
特性 | Struct (结构) | Class (类) |
---|---|---|
类型 | 值类型 (Value Type) | 引用类型 (Reference Type) |
存储位置 | 通常存储在栈(Stack)上<sup>*1</sup>。作为其他对象的成员时,内联存储在堆上。 | 存储在托管堆(Managed Heap) 上。 |
内存分配 | 分配和释放通常是快速、廉价的(栈操作)。 | 分配在堆上,由垃圾回收器(GC)管理回收,有开销。 |
赋值操作 | 复制整个值(创建一个完整的副本)。修改副本不影响原始值。 | 复制引用(复制内存地址)。新旧变量指向同一个对象。修改一个会影响另一个。 |
继承 | 隐式密封(sealed),不能从另一个结构或类继承。 | 通过支持继承(单继承),能够从另一个类继承。 |
默认构造函数 | 不能包含显式的无参构造函数。总是有一个隐式构造函数,将所有字段置为默认值(0, false , null )。 | 能够有无参构造函数。若是没有提供,也会有一个默认的。 |
字段初始化 | 不能在声明时初始化实例字段。 | 可以在声明时初始化实例字段。 |
new 操作符 | new 关键字不是必须的(但推荐使用)。不使用 new 时,必须显式初始化所有字段后才能使用。 | 必须使用new 关键字来创建实例(调用构造函数)。 |
this 的含义 | 在实例方法中,this 是一个值变量(相当于 ref struct )。 | 在实例方法中,this 是一个只读引用。 |
是否可为 null | 不能直接为 null (除非是可空值类型 Nullable<T> 或 MyStruct? )。 | 可以直接赋值为 null 。 |
性能考量 | 适用于小体积、生命周期短的数据。大量赋值操作可能因复制而产生开销。 | 适用于大对象、复杂的对象模型。赋值开销小(只复制引用)。 |
<sup>*1</sup> 关于“栈存储”的重要说明:这是一个常见的简化说法,有助于理解,但并不完全准确。更精确的说法是:结构变量的内存取决于其声明上下文。局部变量和方法参数确实在栈上,但如果结构是另一个类或堆上对象的字段,那么它就会内联存储在那个对象所在的堆上。
代码示例:说明赋值差异
// 定义一个类
public class PointClass
{
public int X { get; set; }
public int Y { get; set; }
}
// 定义一个结构
public struct PointStruct
{
public int X { get; set; }
public int Y { get; set; }
}
class Program
{
static void Main()
{
Console.WriteLine("--- Class ---");
PointClass p1 = new PointClass { X = 10, Y = 20 };
PointClass p2 = p1; // 复制的是引用(内存地址)
p2.X = 100;
Console.WriteLine($"p1.X: {p1.X}"); // 输出 100,因为 p1 和 p2 指向同一个对象
Console.WriteLine("--- Struct ---");
PointStruct s1 = new PointStruct { X = 10, Y = 20 };
PointStruct s2 = s1; // 复制的是整个值(创建一个全新的副本)
s2.X = 100;
Console.WriteLine($"s1.X: {s1.X}"); // 输出 10,s1 和 s2 是两个独立的副本
}
}
如何选择:何时用 Struct?何时用 Class?
使用 struct
的场景(遵循以下所有或大部分原则):
表示轻量级的数据结构,例如坐标点 (
Point
)、复数、RGB 颜色等。逻辑上表示一个单一的值,其行为类似于内置类型(
int
,double
),例如DateTime
、Decimal
。实例大小很小(通常建议在16 字节以下)。
是不可变的(Immutable),创建后状态就不会改变。这是强烈推荐的做法,可以避免很多值复制带来的困惑。
不需要频繁地进行装箱执行(将值类型转换为
object
引用)。
使用 class
的场景:
表示需要标识(Identity)的实体,例如“用户”、“订单”、“产品”。两个对象即使所有数据都相同,它们也是不同的对象。
需要继承和多态。
对象较大或逻辑复杂。
需要为
null
来表示语义上的“无”。
简单总结:class
是“可以做的事情”的抽象,而 struct
是“数据”的抽象。绝大多数情况下,你应该优先选择 class
。只有在满足上述 struct
的使用场景时,才考虑使用它来优化性能。