深入解析 C# 中 const 与 readonly 的核心区别

在 C# 编程中,const 与 readonly 经常被统称为"常量",但二者在初始化规则、编译/运行时行为、IL 生成方式、版本兼容性、引用类型语义 等方面存在本质差异。误用不仅可能引入隐蔽的逻辑错误 ,还会带来库升级后的版本陷阱。


一、初始化位置:编译时强约束 vs 运行时一次性赋值

1️⃣ const:必须声明即赋值(编译期确定)

  • 必须在声明处赋值
  • 值在编译阶段就已确定
  • 任何位置都不能再次赋值(包括构造函数)
cs 复制代码
public class ConstantDemo
{
    public const int MaxRetryCount = 3;
    public const string DefaultTitle = "C#常量解析";

    // ❌ 编译错误:声明时未赋值
    // public const double Pi;

    // ❌ 编译错误:不能在构造函数中修改
    // public ConstantDemo()
    // {
    //     MaxRetryCount = 5;
    // }
}

📌 结论 :const 是"声明即终值"的编译期常量。


2️⃣ readonly:声明时或构造函数中赋值(运行期确定)

  • 可以在声明处赋值
  • 也可以在 实例构造函数 / 静态构造函数 中赋值
  • 每个字段只允许赋值一次
cs 复制代码
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:字段 & 局部变量都支持

cs 复制代码
public class ConstScopeDemo
{
    public const int GlobalConst = 100;

    public void LocalConstDemo()
    {
        const string LocalMsg = "局部常量";
        Console.WriteLine(LocalMsg);
    }
}

readonly:只能修饰字段

cs 复制代码
public class ReadonlyScopeDemo
{
    public readonly int FieldReadonly = 50;

    public void LocalReadonlyError()
    {
        // ❌ 编译错误:readonly 不能修饰局部变量
        // readonly int x = 10;
    }
}

三、编译期 vs 运行期:这是最本质的差异 ⭐⭐⭐

1️⃣ const:值被直接"内联"到 IL 中

cs 复制代码
public const int ConstValue = 10;

public void UseConst()
{
    int a = ConstValue;
}

IL 行为本质:

cs 复制代码
ldc.i4.s 10   // 直接压栈常量 10

⚠️ 重大隐患(版本陷阱):

  • 修改类库中的 const 值
  • 但 引用方未重新编译
  • 引用方仍然使用旧值 ❌

2️⃣ readonly:始终通过字段访问(运行期绑定)

cs 复制代码
public readonly int ReadonlyValue;

public ReadonlyDemo()
{
    ReadonlyValue = 20;
}

public void UseReadonly()
{
    int b = ReadonlyValue;
}

IL 行为本质:

cs 复制代码
ldfld int32 ReadonlyValue

✅ 修改值后,只需重新编译类库即可,调用方无需重新编译。


四、静态语义:隐式静态 vs 显式静态

const:天然 static,且禁止显式声明

cs 复制代码
public class ConstStaticDemo
{
    public const int ConstStatic = 10;

    // ❌ 编译错误
    // public static const int Invalid = 20;
}

调用方式:

cs 复制代码
int x = ConstStaticDemo.ConstStatic;

readonly:默认实例级,静态需显式声明

cs 复制代码
public class ReadonlyStaticDemo
{
    public readonly int InstanceReadonly = 100;
    public static readonly int StaticReadonly = 200;
}

五、引用类型语义:值不可变 vs 引用不可变

const:仅支持 string / null

cs 复制代码
public class ConstReferenceDemo
{
    public const string ConstString = "Hello";
    public const object ConstNull = null;

    // ❌ 编译错误
    // public const List<int> ConstList = new List<int>();
}

原因:

  • const 需要 编译期确定值
  • 除 string 外,引用对象无法编译期确定

readonly:支持任意引用类型(但仅锁引用)

cs 复制代码
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)
  • 永不变化的协议值、枚举值
  • 不会被类库外部依赖引用的内部常量
cs 复制代码
public const int MaxDays = 7;

✅ 推荐使用 readonly 的场景(真实项目更常见)

  • 类库对外暴露的"常量"
  • 配置读取、构造参数注入
  • 引用类型常量(集合、策略对象等)
cs 复制代码
public static readonly string ConnectionString;
static DbConfig()
{
    ConnectionString = LoadFromConfig();
}

八、一句话记忆法(面试 & 实战)

const 是"编译期写死的字面量"
readonly 是"构造期冻结的字段"


结语

const 与 readonly 的差异,本质并不在"能不能改",而在于:

  • 值是在什么时候决定的?(编译期 vs 运行期)
  • 是否参与 IL 内联?
  • 是否影响程序集版本兼容?

在真实工程中:

🔥 99% 对外暴露的"常量",都应该使用 readonly 而不是 const。

理解这一点,你就已经超过了大多数只停留在语法层面的 C# 开发者。

相关推荐
小羊没烦恼!5 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
神仙别闹5 天前
基于C#+MySQL实现(WinForm)个人聊天室软件
c#
kybs19915 天前
全球灾害数据分析可视化 毕业设计-附源码66794
vue.js·spring boot·mysql·安全·django·c#·asp.net
Polevne6 天前
C# SFTP客户端实现文件传输
c#·ssh
samble6 天前
从零搭建灌装监控系统(四):深色主题与样式系统,ResourceDictionary 统一管理
c#·wpf·mvvm·样式·工业控制
cjp5606 天前
024 . WPF MVVM类封装优化
c#
淡海水6 天前
13-04-面试-源码级深度追问链
数据结构·unity·面试·c#·游戏引擎·源码·il2cpp
asdzx676 天前
如何使用 C# 提取 PPT 文本与图片
c#·ppt·提取文本
2501_930707786 天前
使用C#代码使用条件格式为 Excel 隔行设置颜色
c#·excel
何以解忧唯有撸码7 天前
【开源】c#造个轮子-日程安排工具
c#·自定义控件