November 15, 2025 (5mo ago) — last updated December 7, 2025 (4mo ago)

类与结构体:何时使用各自

比较类与结构体:了解何时使用值类型或引用类型、内存与性能的差异,以及用于更好设计的实用规则。

← Back to blog
Cover Image for 类与结构体:何时使用各自

比较类与结构体:了解何时使用值类型或引用类型、内存与性能的差异,以及用于更好设计的实用规则。

类与结构体:何时使用各自

摘要: 比较类与结构体 —— 值与引用、内存、性能,以及帮助你选择正确类型以提高代码效率的设计指导。

介绍

在类与结构体之间做出决定,更多是语义层面的考虑而非语法。关键问题是你是否需要值语义(复制数据)或引用语义(共享身份)。这种差异会影响内存使用、性能、可变性和架构设计。本指南解释这些权衡并给出选择合适类型的实用规则。

核心区别:值语义 vs 引用语义

一位程序员在大屏幕上比较类和结构体的代码块。

当你去掉语言的语法细节,类与结构体的讨论本质上就是值类型与引用类型的对比。把结构体想象成复印的便签本:你把笔记给别人,他们得到自己的一份拷贝。他们可以在自己的拷贝上涂写而不会改变你的原件。这就是值语义——安全、孤立的拷贝。类则像一个共享文档:你发送一个链接,大家编辑的是同一个实时对象。这就是引用语义——共享身份与共享状态。

主要差异一览

特性结构体(值类型)类(引用类型)
数据处理传递时复制数据传递的是引用(指针)
内存分配通常内联或在栈上存放在堆上分配
生命周期短暂、临时的复制品长期存在、可共享的实例
身份由数据相等性定义独立于数据的唯一身份
继承通常不支持继承支持继承和多态
主要使用场景小而自包含的值具有行为的复杂实体

这些原则会对延迟、内存使用和正确性产生实际影响。有意地选择会让你的代码更可预测、更易维护。

内存分配如何影响性能

一个展示栈与堆内存分配差异的图表。

栈和堆是这项决策大部分性能影响的来源。

栈:快速且可预测

栈是一个后进先出(LIFO)的内存区域,函数局部数据在其中入栈和出栈。在栈上分配非常便宜,因为它只是指针运算。对于小型值类型,分配和释放几乎是免费的。

堆:灵活但代价更高

堆允许对象的生命周期超出单次函数调用,但堆分配更慢,可能触发垃圾回收或需要手动释放。引用类型引入了额外的间接层:栈上保存的是指向堆数据的指针。频繁的堆分配会增加 GC 压力,并可能在托管运行时中引起停顿1

C# 示例

// Value Type - lives inline (often on the stack)
public struct PointStruct {
    public int X;
    public int Y;
}

// Reference Type - object lives on the heap
public class PointClass {
    public int X;
    public int Y;
}

public void ProcessPoints() {
    PointStruct p1 = new PointStruct { X = 10, Y = 20 };
    PointClass p2 = new PointClass { X = 10, Y = 20 };
}

在紧密循环中,为小对象进行成千上万次的堆分配会显著增加 GC 活动;数组中的结构体通常能获得更好的缓存局部性并降低 GC 压力2

各语言如何处理类与结构体

一屏分割显示不同编程语言的代码片段,示意类与结构体。

不同语言侧重不同的默认行为。最佳选择既取决于原始性能,也取决于语言习惯用法。

C++:关键字几乎相同

在 C++ 中,structclass 几乎相同;唯一的技术性差别是默认访问权限(struct 为 public,class 为 private)。对纯数据聚合使用 struct,对封装类型和复杂行为使用 class3

C#:明确的值/引用区分

C# 将区分明确化:struct 是真正的值类型,而 class 是引用类型。对小且不可变的值(坐标、颜色)使用结构体,对具有身份和可变共享状态的实体使用类。

Swift:偏好值类型

Swift 倾向于先使用值。当心:Apple 的指导和 Swift 社区默认偏向使用 struct,并仅在需要引用语义(例如共享可变状态或与 Objective-C API 交互)时使用 class4

Rust:所有权与安全性

Rust 使用 struct 再配合所有权和借用模型,在没有垃圾收集器的情况下提供内存安全。行为通过 impl 块附加,编译器在编译时强制执行所有权和借用规则,从而在运行前防止许多共享状态的错误5

struct Player {
    username: String,
    level: u32,
    is_active: bool,
}

impl Player {
    fn level_up(&mut self) {
        self.level += 1;
    }
}

Rust 的方法让你在获得直接内存控制的性能的同时,享受编译期的安全保证。

何时选择结构体以获得更好性能

当类型较小、自包含、并且被当作值而非身份来处理时,选择结构体。典型候选:

  • 几何点(Point2D)
  • 颜色值(RGB/RGBA)
  • 小型配置负载
  • 轻量级计算输入

好处包括更少的堆分配、更好的缓存局部性,以及在托管运行时中更低的 GC 压力。由于内存访问模式对 CPU 更友好,改进的缓存局部性在数据密集型循环中可以带来显著的速度提升2

何时选择类以建模复杂行为

当对象具有稳定的身份、共享可变状态,或需要继承或复杂生命周期管理时,选择类。典型候选:

  • 用户配置文件或领域实体
  • 数据库或网络连接对象
  • 协调状态的服务和管理器

类是许多面向对象模式的基础。继承和多态使得建模复杂关系和行为更加容易。

决策清单:结构体 vs 类

考量使用结构体(值)使用类(引用)
身份数据就是身份对象具有唯一身份
可变性不可变或小型、孤立状态共享的、可变的状态
行为与数据绑定的简单逻辑复杂交互和行为
生命周期短暂、局部作用域长期、全局/应用级
共享复制是安全的必须通过引用共享

常见问题

结构体可以有方法吗?

可以。像 C#、Swift 和 Rust 这样的现代语言允许结构体拥有方法、初始化器,以及实现协议或接口。主要区别仍然在于它们如何被复制和传递。

结构体总是更快吗?

不一定。小型结构体通常比堆分配的对象更快,但大型结构体复制开销可能很高。始终进行测量:在对真实工作负载做出重大更改前先进行性能分析。

结构体支持继承吗?

通常不支持。结构体很少支持继承,但许多语言允许结构体实现接口或协议,从而在不依赖深度继承链的情况下实现灵活的组合。

实用问答

问:我应该何时将类重构为结构体?

答:当该类型是小型、不可变且没有唯一身份时,重构为结构体可以减少堆分配并提供更清晰的值语义。

问:在托管语言中如何避免 GC 停顿?

答:通过优先对小值使用结构体、重用对象和使用对象池来减少短生命周期堆分配;在负载下测量 GC 行为以优化1

问:最简单的经验法则是什么?

答:如果对象表示“一个值”,使用结构体;如果表示“具有身份的事物”,使用类。

三个简明的问答小节

问答 1 — 性能权衡

问:切换到结构体是否总能提升速度? 答:不是。对小且频繁创建的值使用结构体可以减少 GC 压力并改善缓存局部性;避免大型结构体因为复制代价高而带来负面影响。

问答 2 — 安全性与正确性

问:结构体能减少因共享状态引起的错误吗? 答:能。值语义可以防止意外的共享可变性,当值被复制而非共享时,可以减少并发和状态相关的错误。

问答 3 — 设计与架构

问:什么时候类比结构体更合适? 答:当需要身份、长期生命周期或继承与多态时,使用类更合适。


在 Clean Code Guy,我们帮助团队重构以实现可扩展性和可维护性。了解更多请访问 https://cleancodeguy.com。

1.
Microsoft Docs. “Garbage collection performance and tuning.” https://learn.microsoft.com/dotnet/standard/garbage-collection/
2.
Ulrich Drepper. “What Every Programmer Should Know About Memory.” https://people.freebsd.org/~lstewart/articles/cpumemory.pdf
3.
cppreference.com. “class vs struct.” https://en.cppreference.com/w/cpp/language/class
4.
Apple. “Structures and Classes” (Swift Programming Language). https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html
5.
The Rust Programming Language. “Ownership.” https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html
← Back to blog
🙋🏻‍♂️

AI编写代码。
您让它持久。

在AI加速的时代,干净代码不仅仅是好的实践 — 它是能够扩展的系统与在自己的重量下崩溃的代码库之间的区别。