在日常开发中,const 和 readonly 都被用来表示“不可变”的值。很多人觉得它们差不多,甚至随手就选一个用。但在真实项目里,尤其是做公共库、NuGet 包或多项目解决方案时,选错可能会埋下隐患——轻则行为不一致,重则版本升级后出现难以排查的问题。
它们的核心差异,其实在于一句话:
const 是编译时常量,readonly 是运行时只读字段。
理解了这点,后面的区别就顺理成章了。
const:编译时常量
const 表示编译期常量。也就是说,它的值在编译时就已经确定,并被直接“写死”进程序集里。
它有几个明显特征:
必须在声明时赋值; 只支持基元类型(如 int、double、bool)和string;所有引用该常量的地方,都会在编译时被替换为字面量; 性能极高,因为运行时根本不需要读字段; 如果它存在于共享库中,一旦修改,所有依赖项目都必须重新编译,否则仍然使用旧值①。
典型用法如下:
public const double Pi = 3.14159;
public const int MaxRetryCount = 3;当你在别的类中使用 MaxRetryCount 时,编译器会直接把它替换成 3,而不是去读取某个字段。
这也是 const 的优势——简单、纯粹、极致高效。
但也正因为如此,它在跨程序集场景下会带来版本风险。
readonly:运行时只读字段
readonly 表示运行时只读字段。它的值可以在声明时赋值,也可以在构造函数中赋值,但一旦对象构造完成,就不能再修改。
它的特性包括:
可以在声明处或构造函数中赋值; 支持任意类型,包括对象、结构体、集合等; 值存储在内存中,运行时通过字段访问; 修改字段值后,不要求依赖方重新编译②。
示例:
public readonly string ApiBaseUrl;
public Config()
{
ApiBaseUrl = Environment.GetEnvironmentVariable("API_URL")
?? "https://api.example.com";
}
这里的 ApiBaseUrl 是在运行时根据环境变量确定的,显然无法使用 const。
与 const 不同的是,readonly 不会被编译器内联替换,而是通过字段访问。这意味着它的值在程序集之间是“动态解析”的,更安全,也更灵活。
核心差异对比
constreadonly
这个表格,基本可以当作选型速查表。
底层 IL 行为差异
在 IL 层面,两者的实现方式完全不同③。
const会被编译器替换为字面量,例如:
ldc.i4.s 100
readonly则会生成字段读取指令,例如:
ldfld int32 MyClass::MaxItems
这意味着什么?
假设你在一个 NuGet 包里写了:
public const int Timeout = 30;
后来你把它改成 60 并重新发布。但如果调用方没有重新编译,它仍然会使用 30。因为在它的程序集里,早就被替换成了字面量 30。
而如果使用的是 readonly,调用方运行时会读取字段值,自然就能拿到 60。
这就是为什么在公共库设计中,要对 const 保持警惕。
实际应用场景建议
场景一:数学或协议常量
这种值永远不会改变,比如 HTTP 状态码、数学常量,适合使用 const。
public const int HttpStatusCodeOk = 200;
public const string JwtIssuer = "MyApp";
前提是:你百分之百确定它不会变化。
场景二:依赖环境或配置的值
只要和运行环境有关,就不要用 const。
public static readonly string DatabaseConnectionString =
Configuration.GetConnectionString("Default");
这类值天然是运行时决定的。
场景三:对象或复杂结构
const 不支持引用类型,因此只能使用 readonly。
public readonly List<string> SupportedFormats =
new() { "json", "xml" };
不过需要注意:readonly 限制的是字段引用不可变,不代表集合内容不可变。
场景四:共享库中的公共常量
这是最容易踩坑的地方。
如果常量对外公开,并且存在未来调整的可能,**优先使用 readonly**。即便当前值看起来固定,也要考虑版本演进问题④。
很多成熟框架在设计 API 时,都会刻意避免在公共接口中暴露 public const。
结语
const 与 readonly 的区别,本质是:
const追求编译期确定性与极致性能;readonly提供运行时灵活性与版本安全。
在普通业务代码中,两者差异可能不明显;但在框架设计、公共库开发或多项目协作中,这个选择会直接影响系统的可维护性。
一句经验之谈可以作为总结:
如果这个值真的永远不变,用 const; 只要存在一丝变化可能,就用 readonly。
写代码不仅是语法选择,更是架构决策。
参考资料
① Microsoft. const (C# Reference)https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/const
② Microsoft. readonly (C# Reference)https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/readonly
③ ECMA-335. Common Language Infrastructure (CLI) Standardhttps://www.ecma-international.org/publications-and-standards/standards/ecma-335/
④ Jeffrey Richter. CLR via C#, 4th Edition. Microsoft Press, 2014.