在 C# 开发里,很多“不会变的值”和“有限状态集合”看起来都像固定数据,但它们并不适合用同一种方式表达。本文把 const、readonly 和 enum 放到同一条线上说明,帮助你判断什么时候该用编译期常量,什么时候该把业务状态抽成独立类型,同时理解它们在可读性、类型安全和底层表示上的差异。
常量 const 适合什么场景
常量指的是在程序编译时就已经确定值,并且在整个运行期间都不能修改的数据。对于这类值,C# 使用 const 关键字声明。

声明常量时有两个关键点:
- 必须使用
const关键字。 - 必须在声明时立即初始化。
const double PI = 3.14159;
const int DAYS_IN_WEEK = 7;
const string AppVersion = "1.0.2";
这类写法适合表达数学常量、固定业务参数、不会随环境变化的版本标识等内容。它的核心价值不只是“不能改”,更重要的是让编译器和阅读代码的人都明确知道:这个值从设计上就是固定的。
命名上,常量通常使用全大写加下划线分隔单词的形式,以便和普通变量区分,例如 DAYS_IN_WEEK。当然,实际项目里也要以团队代码规范为准。
const 和 readonly 到底差在哪
const 与 readonly 经常一起出现,因为它们都带有“只读”的意味,但适用条件并不相同。
const:值必须在编译时确定。readonly:值可以在运行时确定,通常在构造函数中赋值,赋值后不能再改。
也就是说,如果某个值只有在对象创建时才能拿到,例如创建时间、配置加载结果、外部环境相关参数,那么它就不适合写成 const,而更适合使用 readonly。
public class Config
{
// 编译时常量
public const int DefaultTimeout = 5000;
// 运行时只读字段
public readonly DateTime CreationTime;
public Config()
{
// readonly 字段可以在构造函数中初始化
CreationTime = DateTime.Now;
}
}
判断时可以抓住一个简单原则:如果值在写代码时就已经确定,用 const;如果值要等程序运行、对象实例化或构造过程才知道,用 readonly。
枚举 enum 为什么比数字状态更合适
当一段数据不是“单个固定值”,而是“若干个有限且明确的取值”时,更适合使用枚举,也就是 enum。
枚举本质上是一组命名常量的集合,而且这个集合本身会成为一个新的类型。它特别适合表示订单状态、错误码等级、流程阶段、用户角色这类范围有限的业务值。
比如下面这个订单状态枚举:
// 定义一个订单状态的枚举
public enum OrderStatus
{
Pending, // 待处理
Processing, // 处理中
Shipped, // 已发货
Completed, // 已完成
Cancelled // 已取消
}
定义完成后,OrderStatus 就不再只是几个分散的标记,而是一个可以直接参与变量声明、条件判断和分支控制的正式类型。
// 声明一个枚举类型的变量
OrderStatus currentStatus = OrderStatus.Pending;
// 在逻辑判断中使用,代码清晰易读
if (currentStatus == OrderStatus.Shipped)
{
Console.WriteLine("订单已发货!");
}
// 在 switch 语句中使用,是枚举的最佳拍档
switch (currentStatus)
{
case OrderStatus.Pending:
// ...
break;
case OrderStatus.Processing:
// ...
break;
// ... 其他状态
default:
// ...
break;
}
相比直接使用数字状态码,枚举最大的提升在于业务语义被直接写进了代码里。看到 OrderStatus.Completed,你不需要额外查表就知道含义;如果只看到数字 3,上下文就会模糊很多。
枚举在实际开发中的三个优势
枚举之所以常被推荐,不只是写法整齐,更因为它在工程实践里有很明显的收益。
1. 强类型安全
枚举变量只能接收该枚举类型定义范围内的值,编译器会帮助你拦截明显不合理的赋值。这样可以减少把无效状态码写入业务变量的风险,例如把 orderStatus = 10 这种不合法状态混进流程里。
2. 代码可读性更高
命名后的状态本身就带有语义,代码更接近业务表达。团队协作、代码审查和后续维护时,这种优势会非常明显,尤其是在条件分支较多的模块里。
3. IDE 智能提示更友好
在 Visual Studio 等 IDE 中,输入枚举变量后通常会自动列出可选成员。这样既能减少拼写错误,也能让开发者更快了解当前可用的状态范围。
如果把上面三点合在一起看,枚举其实解决的是两个问题:一是减少无意义数字带来的出错空间,二是让代码表达更接近真实业务语义。
枚举的底层值和自定义方式
虽然枚举主要是给人读、给编译器约束,但它在底层仍然和数值有关。默认情况下,枚举成员会对应一组整数值,并且从 0 开始依次递增。

以 OrderStatus 为例,默认映射关系就是:
Pending是0Processing是1Shipped是2Completed是3Cancelled是4
如果业务上需要与既有协议、接口返回码或数据库字段对齐,也可以显式指定成员值,甚至修改底层类型。默认底层类型是 int,但并不局限于它。
public enum ErrorCode : ushort // 可以指定底层类型
{
None = 0,
NotFound = 404,
InternalServerError = 500,
AccessDenied = 403
}
这类写法常见于错误码、协议字段、设备状态标识等场景。它的意义在于:你既保留了枚举带来的可读性和类型约束,又能和外部系统使用的固定数值保持一致。
该用 const、readonly 还是 enum
三者并不是互相替代的关系,而是分别解决不同问题。
- 当你要表达一个在编译期就确定、永远不会变化的值时,用
const。 - 当你要表达一个只能在运行时确定,但初始化后不应再改变的字段时,用
readonly。 - 当你要表达一组有限、可枚举、带明确业务含义的状态或类别时,用
enum。
实际编码时,最常见的误区就是把状态值也写成若干个数字常量。这样虽然也能运行,但会损失类型信息和可读性。只要数据本质上是“某一组离散选项中的一个”,枚举通常就是更合适的表达方式。
把这几个关键字分清之后,代码结构会更稳定:固定参数用常量,实例级只读数据用 readonly,业务状态集合用枚举。这样既利于维护,也能让后续逻辑判断更清楚。









