在 c# 编程中,const 与 readonly 经常被统称为“常量”,但二者在初始化规则、编译/运行时行为、il 生成方式、版本兼容性、引用类型语义等方面存在本质差异。误用不仅可能引入隐蔽的逻辑错误,还会带来库升级后的版本陷阱。
一、初始化位置:编译时强约束 vs 运行时一次性赋值
1️⃣const:必须声明即赋值(编译期确定)
- 必须在声明处赋值
- 值在编译阶段就已确定
- 任何位置都不能再次赋值(包括构造函数)
public class constantdemo
{
public const int maxretrycount = 3;
public const string defaulttitle = "c#常量解析";
// ❌ 编译错误:声明时未赋值
// public const double pi;
// ❌ 编译错误:不能在构造函数中修改
// public constantdemo()
// {
// maxretrycount = 5;
// }
}
📌 结论:const 是“声明即终值”的编译期常量。
2️⃣readonly:声明时或构造函数中赋值(运行期确定)
- 可以在声明处赋值
- 也可以在 实例构造函数 / 静态构造函数 中赋值
- 每个字段只允许赋值一次
public class readonlydemo
{
// 声明时赋值
public readonly int minage = 18;
// 构造函数中赋值
public readonly int userid;
public readonlydemo(int userid)
{
userid = userid;
}
// 静态 readonly:在静态构造函数中赋值
public static readonly string version;
static readonlydemo()
{
version = "1.0.1";
}
}
📌 结论:readonly 是“构造期冻结”的运行时常量。
二、修饰对象范围:字段 + 局部变量 vs 仅字段
const:字段 & 局部变量都支持
public class constscopedemo
{
public const int globalconst = 100;
public void localconstdemo()
{
const string localmsg = "局部常量";
console.writeline(localmsg);
}
}
readonly:只能修饰字段
public class readonlyscopedemo
{
public readonly int fieldreadonly = 50;
public void localreadonlyerror()
{
// ❌ 编译错误:readonly 不能修饰局部变量
// readonly int x = 10;
}
}
三、编译期 vs 运行期:这是最本质的差异 ⭐⭐⭐
1️⃣const:值被直接“内联”到 il 中
public const int constvalue = 10;
public void useconst()
{
int a = constvalue;
}
il 行为本质:
ldc.i4.s 10 // 直接压栈常量 10
⚠️ 重大隐患(版本陷阱):
- 修改类库中的 const 值
- 但 引用方未重新编译
- 引用方仍然使用旧值 ❌
2️⃣readonly:始终通过字段访问(运行期绑定)
public readonly int readonlyvalue;
public readonlydemo()
{
readonlyvalue = 20;
}
public void usereadonly()
{
int b = readonlyvalue;
}
il 行为本质:
ldfld int32 readonlyvalue
✅ 修改值后,只需重新编译类库即可,调用方无需重新编译。
四、静态语义:隐式静态 vs 显式静态
const:天然 static,且禁止显式声明
public class conststaticdemo
{
public const int conststatic = 10;
// ❌ 编译错误
// public static const int invalid = 20;
}
调用方式:
int x = conststaticdemo.conststatic;
readonly:默认实例级,静态需显式声明
public class readonlystaticdemo
{
public readonly int instancereadonly = 100;
public static readonly int staticreadonly = 200;
}
五、引用类型语义:值不可变 vs 引用不可变
const:仅支持string/null
public class constreferencedemo
{
public const string conststring = "hello";
public const object constnull = null;
// ❌ 编译错误
// public const list<int> constlist = new list<int>();
}
原因:
const需要 编译期确定值- 除
string外,引用对象无法编译期确定
readonly:支持任意引用类型(但仅锁引用)
public class readonlyreferencedemo
{
public readonly list<int> numbers = new() { 1, 2, 3 };
public void modify()
{
numbers.add(4); // ✅ 合法
// ❌ 编译错误:不能重新赋值
// numbers = new list<int>();
}
}
⚠️ readonly ≠ 不可变对象
- 锁的是 引用地址
- 不是对象内容
六、完整对比速查表
| 维度 | const | readonly |
|---|---|---|
| 初始化时机 | 编译期 | 运行期 |
| 赋值位置 | 仅声明处 | 声明 / 构造函数 |
| 修饰对象 | 字段 + 局部变量 | 仅字段 |
| 静态特性 | 默认 static | 默认实例级 |
| il 行为 | 内联常量 | 字段访问 |
| 引用类型 | string / null | 任意引用类型 |
| 版本安全 | ❌ 易出问题 | ✅ 安全 |
七、工程化使用建议(非常重要)
✅ 优先使用const的场景
- 数学常量(
pi、e) - 永不变化的协议值、枚举值
- 不会被类库外部依赖引用的内部常量
public const int maxdays = 7;
✅ 推荐使用readonly的场景(真实项目更常见)
- 类库对外暴露的“常量”
- 配置读取、构造参数注入
- 引用类型常量(集合、策略对象等)
public static readonly string connectionstring;
static dbconfig()
{
connectionstring = loadfromconfig();
}
八、一句话记忆法(面试 & 实战)
const 是“编译期写死的字面量”
readonly 是“构造期冻结的字段”
结语
const 与 readonly 的差异,本质并不在“能不能改”,而在于:
- 值是在什么时候决定的?(编译期 vs 运行期)
- 是否参与 il 内联?
- 是否影响程序集版本兼容?
在真实工程中:
🔥 99% 对外暴露的“常量”,都应该使用 readonly 而不是 const。
理解这一点,你就已经超过了大多数只停留在语法层面的 c# 开发者。
到此这篇关于浅谈c# 中 const 与 readonly的核心区别的文章就介绍到这了,更多相关c# const readonly内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论