c# const 和 readonly 的区别

来源:这里教程网 时间:2026-02-21 17:37:24 作者:

const 只能在编译时确定值,改了就得全量重编译

如果你把一个配置写成

const string ApiUrl = "https://api.example.com";
,那这个字符串不是“存在内存里”,而是被 C# 编译器直接**替换进所有调用它的 IL 代码中**。这意味着:一旦你更新了这个常量、发布新版本的类库,但调用方没重新编译——它还在用旧地址。

常见错误现象:
ApiUrl
在类库中已改成
"https://new-api.example.com"
,但老客户端仍请求旧地址,且日志/调试都看不出问题
适用场景:数学常量(
const double Pi = 3.14159;
)、固定协议标识(
const string ContentTypeJson = "application/json";
)这类真正“永不变”的值
不能用于需要运行时计算的值,比如
DateTime.Now.ToString()
Environment.GetFolderPath(...)
—— 编译器直接报错

readonly 支持运行时赋值,能用在构造函数和复杂类型上

readonly
字段不是“编译期硬编码”,而是在对象创建过程中(声明时或构造函数里)一次性赋值,之后禁止修改。它不挑类型,也不要求“编译期可算”。

支持任意类型:数组、自定义类、
DateTime
Guid
、甚至
HttpClient
实例(只要确保只初始化一次)
可以是实例级或静态级:
readonly string InstanceId
每个对象不同;
static readonly Guid AppId
全局一份
常见错误:在普通方法里试图赋值
this.Timeout = 30;
→ 编译错误 CS0198:“无法对只读字段赋值”
public class ServiceClient
{
    public readonly int Timeout;
    public readonly DateTime CreatedAt;
    public readonly HttpClient Http;
<pre class='brush:php;toolbar:false;'>public ServiceClient(int timeout)
{
    Timeout = timeout;              // ✅ 构造函数中赋值 OK
    CreatedAt = DateTime.UtcNow;   // ✅ DateTime 支持
    Http = new HttpClient();       // ✅ 引用类型也 OK
}

}

const 隐含 static,readonly 默认是实例级

你写

const int MaxRetries = 3;
,它天然就是类级别的,只能通过
MyClass.MaxRetries
访问,不能用实例去点 —— 即使写了
var x = new MyClass(); x.MaxRetries
,编译器也会报错。

readonly
字段默认属于实例:每个对象都有自己的一份副本(比如缓存路径、用户 ID 等)
要让它变成类级别?加
static readonly
,例如:
static readonly string ConfigPath = Path.Combine(AppContext.BaseDirectory, "config.json");
不能写
static const
—— 语法错误,因为
const
已经是静态的了

选哪个?看三件事:能不能编译期确定、类型是否受限、要不要跨程序集安全更新

这不是风格偏好,而是行为差异带来的实际后果。尤其当你在写 NuGet 包或基础类库时,选错会埋坑。

值是
int
/
string
/
enum
,且确认永远不变 → 用
const
(性能略高,无内存开销)
值依赖构造参数、环境变量、配置文件、或类型是
List<t></t>
/
DateTime
/
object
→ 必须用
readonly
要发布给外部项目用?优先选
static readonly
而非
const
,否则别人升级你的包却没重编译,就会拿到过期值

容易被忽略的一点:即使你用

readonly
,也不能阻止反射强行修改(
FieldInfo.SetValue(...)
),但它至少守住编译期和常规运行时的契约。而
const
的“不可变”是编译器强制内联的结果,连反射都改不了——因为根本没字段存在。

相关推荐