×

90% 的人答不清:C#中const 和 readonly 有什么区别?

独孤求败 独孤求败 发表于2026-02-25 09:44:14 浏览8 评论0

抢沙发发表评论

在日常开发中,const 和 readonly 都被用来表示“不可变”的值。很多人觉得它们差不多,甚至随手就选一个用。但在真实项目里,尤其是做公共库、NuGet 包或多项目解决方案时,选错可能会埋下隐患——轻则行为不一致,重则版本升级后出现难以排查的问题。

它们的核心差异,其实在于一句话:

const 是编译时常量,readonly 是运行时只读字段。

理解了这点,后面的区别就顺理成章了。


const:编译时常量

const 表示编译期常量。也就是说,它的值在编译时就已经确定,并被直接“写死”进程序集里。

它有几个明显特征:

  • 必须在声明时赋值;
  • 只支持基元类型(如 intdoublebool)和 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.


群贤毕至

访客