const

constreadonly 都用于定义"不可变"的值,但它们的实现机制、使用时机和适用场景有本质区别。下面详细对比。


📋 核心区别总览

对比项constreadonly
本质编译时常量运行时只读字段
赋值时机声明时必须赋值声明时或构造函数中赋值
所属层级隐式静态(属于类型)实例级(也可加 static
值存储位置嵌入到调用方代码中存储在对象/类型内存中
支持类型仅限基元类型、string、enum、null任意类型(包括对象、数组等)
访问方式ClassName.Memberinstance.MemberClassName.Member
版本兼容性修改后需重新编译所有引用方修改后无需重编译引用方

1. const — 编译时常量

核心特征:值在编译时确定,编译器会将常量的值直接"内联"到所有引用它的地方。

using System;

class MathConstants
{
    public const double PI = 3.14159265358979;
    public const string AppName = "MyApp";
    public const int MaxRetry = 3;

    // ❌ 编译错误!不能在构造函数中赋值
    // public const double E;
    // static MathConstants() { E = 2.718; }
}

class Program
{
    static void Main()
    {
        // 直接使用,无需创建实例
        Console.WriteLine($"PI = {MathConstants.PI}");           // 输出: PI = 3.14159265358979
        Console.WriteLine($"App = {MathConstants.AppName}");     // 输出: App = MyApp

        // 编译器实际生成的代码等价于:
        // Console.WriteLine($"PI = {3.14159265358979}");  ← 值已被内联
    }
}

const 的限制

class Example
{
    // ✅ 支持的类型
    public const int Number = 100;
    public const string Text = "hello";
    public const double Pi = 3.14;
    public const bool Flag = true;
    public const DayOfWeek Day = DayOfWeek.Monday;  // enum
    public const object Null = null;

    // ❌ 不支持的类型(编译错误)
    // public const DateTime Today = DateTime.Now;   // 非编译时常量
    // public const int[] Arr = { 1, 2, 3 };        // 数组不行
    // public const List<int> List = new();          // 引用类型不行
}

2. readonly — 运行时只读字段

核心特征:值在运行时确定,只能在声明时或构造函数中赋值,之后不可修改。

using System;

class Config
{
    // 声明时赋值
    public readonly string Version = "1.0.0";

    // 构造函数中赋值(每个实例可以不同)
    public readonly DateTime CreatedAt;
    public readonly int InstanceId;

    public Config(int id)
    {
        CreatedAt = DateTime.Now;   // ✅ 构造函数中可以赋值
        InstanceId = id;
    }

    public void TryModify()
    {
        // ❌ 编译错误!readonly 字段不能在普通方法中修改
        // Version = "2.0.0";
        // InstanceId = 999;
    }
}

class Program
{
    static void Main()
    {
        Config c1 = new Config(1);
        Config c2 = new Config(2);

        Console.WriteLine($"c1: Id={c1.InstanceId}, Time={c1.CreatedAt}");
        Console.WriteLine($"c2: Id={c2.InstanceId}, Time={c2.CreatedAt}");
        // 两个实例的 CreatedAt 和 InstanceId 各不相同
    }
}

static readonly 的用法

using System;

class AppConfig
{
    // static readonly:整个类型共享一份,在静态构造函数中初始化
    public static readonly string ConnectionString;
    public static readonly DateTime StartupTime;

    static AppConfig()
    {
        // 适合放运行时才能确定的全局配置
        ConnectionString = Environment.GetEnvironmentVariable("DB_CONN") ?? "default";
        StartupTime = DateTime.Now;
    }
}

class Program
{
    static void Main()
    {
        Console.WriteLine($"启动时间: {AppConfig.StartupTime}");
        Console.WriteLine($"连接串: {AppConfig.ConnectionString}");
    }
}

⚠️ const 的版本兼容性陷阱

这是两者最容易被忽视的区别:

// ===== 程序集 A(类库)=====
public class Constants
{
    public const int Version = 1;
}

// ===== 程序集 B(引用 A)=====
Console.WriteLine(Constants.Version);
// 编译器实际生成的 IL 代码是:Console.WriteLine(1);  ← 值被内联了

如果之后将程序集 A 中的 Version 改为 2,只重新编译 A 而不重编译 B,B 仍然输出 1!因为 B 的代码里已经硬编码了 1

readonly 不存在这个问题,因为它是运行时从内存中读取的。


🔑 选择指南

场景推荐
值是编译时就能确定的固定常量(如数学常量、枚举值)const
值需要在运行时计算(如读取配置、当前时间)readonly
类型是 DateTime、数组、自定义对象等复杂类型readonly
希望每个实例有不同的只读值readonly(实例级)
希望全局共享且运行时初始化static readonly
值可能被未来版本修改,且不希望引用方重新编译readonly

实践建议:除非你确定值永远不会变且是基元类型,否则优先使用 static readonly 代替 constconst 的内联特性虽然性能略好,但带来的版本兼容性风险往往得不偿失。这也是很多团队编码规范中"禁止使用 const,统一用 static readonly"的原因。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值