C# 系列教程_5 常量 系统精讲(新手友好・详尽版)
前置回顾变量variable内存命名空间值可以修改。常量constant命名空间值一旦定义永远不能修改。C# 里面有两种最核心的 “只读” 成员很多新手会混淆const——编译期常量真正意义上的常量readonly——运行期只读字段不是常量是只读成员还有 C# 7.0 新增的readonly struct只读结构体以及get只读属性后面一并对比。核心一句话区分 ✅const编译的时候就把值写死在代码里编译期确定。 ✅readonly运行时创建对象的时候赋值一次之后不能修改。学习路线const 常量基础语法、示例、机理const 使用规则、能放哪些类型、不能放哪些类型readonly 只读字段对比 const静态 readonly、实例 readonlyreadonly struct 只读结构体拓展常量 / 只读字段 内存模型使用场景怎么选什么时候用 const什么时候用 readonly新手高频坑 代码练习1 const 编译期常量const constant1.1 基础语法const 数据类型 常量名 常量值;规范常量命名一般全部大写单词下划线分隔MAX_COUNT行业通用规范。示例 1最简单 const 常量using System; namespace ConstDemo { class Program { // 定义常量写在类里面 public const double PI 3.1415926; static void Main(string[] args) { // 读取常量 double r 5; double area PI * r * r; Console.WriteLine(圆面积 area); // PI 999; // 编译报错常量不能赋值修改 } } }机理拆解非常关键public const double PI 3.1415926;const 是编译期常量。编译器在编译代码的时候直接把代码中所有 PI 替换成字面量 3.1415926。编译完成后程序集里面不再存在一个叫 PI 的变量代码里所有 PI 都被直接替换成数字。读取PI * r * r编译后等价于3.1415926 * r * r尝试写PI 999;编译器直接拦截常量不允许赋值编译失败程序无法运行。本质const 不是内存里一块可以读写的存储空间是代码文本替换。1.2 const 可以写在什么位置✅ 可以写类内部最常用方法内部局部常量C# 支持❌ 不能写在属性里面不能写在方法参数上。示例 2方法内局部 conststatic void Main(string[] args) { const int MAX_AGE 120; int userAge 20; if(userAge MAX_AGE) { Console.WriteLine(年龄合法); } // MAX_AGE 200; //编译报错 }局部常量只在当前{}代码块内有效作用域和局部变量一致。1.3 const 允许的数据类型重点考点const只能是编译期就能确定值的类型基础值类型bool, char, byte, short, int, long, float, double, decimalstring字符串null引用类型常量只能是 null❌不能用 const 修饰 class、数组、自定义 struct 对象。原因new 创建对象是运行时操作编译阶段拿不到对象实例。错误示例// const Student s new Student(); //编译报错不能new作为const值string 特例const string GREET Hello; // ✅合法 const string EMPTY null; // ✅合法1.4 const 隐式是 staticconst默认自带static特性不需要手动写 static。 访问方式类名.常量名不需要 new 对象。class MathHelper { public const double PI 3.1415926; } static void Main() { Console.WriteLine(MathHelper.PI); //直接类名访问不用new MathHelper }就算你写static const编译器也允许但属于多余代码不推荐。1.5 const 的优缺点✅ 优点编译期直接文本替换没有运行时开销性能最高防止代码中 “魔法数字”散落在代码里的裸数字可读性差统一管理修改只需要改一处全部自动更新。❌ 缺点编译期固定如果是跨项目引用修改常量后依赖项目必须重新编译只能存放编译期常量不能放运行时计算出来的值。魔法数字举例 差代码if(score 60)60 就是魔法数字 好代码const int PASS_SCORE 60; if(score PASS_SCORE)含义一目了然后期改及格线只需要修改常量。2 readonly 只读字段运行期只读经常和 const 对比⚠️ 重点readonly不是常量是实例 / 静态字段只是只读。readonly 字段是分配在内存的运行时才有值编译期不会做文本替换。语法readonly 类型 字段名;规则只能在声明时赋值或者构造函数里面赋值对象构造完成之后任何地方都不能再次修改示例 1实例 readonly属于对象实例using System; namespace ReadOnlyDemo { class Book { // 只读实例字段 public readonly string BookName; public readonly double Price; // 构造函数创建对象的时候可以给readonly赋值 public Book(string name, double price) { BookName name; Price price; } } class Program { static void Main(string[] args) { Book b1 new Book(C#入门, 59.9); Console.WriteLine(${b1.BookName}, {b1.Price}); // b1.Price 99; //编译报错构造完成后readonly字段禁止修改 } } }机理拆解BookName、Price是实例只读字段存在堆内存对象内部new Book()创建对象时进入构造函数给这两个字段赋值构造函数执行完毕对象创建完成这两个字段就锁死不能再写运行时读取内存中的值编译器不会做文本替换。和 const 巨大区别 const 编译期替换readonly 是堆内存字段运行时读取内存。示例 2static readonly 静态只读字段属于类class Config { //静态只读字段属于类不属于对象 public static readonly DateTime BuildTime DateTime.Now; } static void Main() { Console.WriteLine(Config.BuildTime); // Config.BuildTime DateTime.Now; //报错不能修改 }这个场景不能用 constDateTime.Now是程序运行那一刻才能获取的时间编译期不知道所以只能用static readonly。 ✅ 核心使用场景值需要运行时计算、读取但是之后永不修改。const vs static readonly 对比表项目conststatic readonly类型编译期常量运行期只读静态字段赋值时机声明时必须赋值声明处 / 静态构造函数赋值本质编译代码文本替换无内存变量内存中分配字段运行读取支持类型仅限编译期常量int/string 等任意类型包括对象、DateTime跨程序集更新修改后所有引用项目要重新编译修改后不用重编译依赖项目性能略高无内存读取略低运行读取内存一句话记忆编译期就确定死的值用 const运行时才能拿到的值用 static readonly。3 readonly struct 只读结构体拓展readonly struct整个结构体全部只读结构体内部字段都不能修改。readonly struct Point { public readonly int X; public readonly int Y; public Point(int x, int y) { X x; Y y; } }作用防止结构体实例被意外修改值类型优化场景使用。新手初学暂时用得少。4 只读属性get-only 自动属性另一种只读方案C# 可以用属性只写 get不写 set实现只读class Student { public string Name { get; } //只读自动属性只能在构造函数赋值 public Student(string name) { Name name; } }本质编译器自动生成一个 readonly 后台字段。现在写项目很多时候优先用只读属性代替 readonly 字段封装性更好。5 常量的作用与实际价值消除魔法数字提高代码可读性// 不好 if(score 90) Console.WriteLine(优秀); // 好 const int EXCELLENT_SCORE 90; if(score EXCELLENT_SCORE) Console.WriteLine(优秀);读代码一眼看懂 90 代表什么后期修改只改常量一处。统一维护多处使用同一个数值避免多处修改漏改防止意外修改编译器强制保护任何地方尝试修改直接编译报错保护关键参数。6 新手高频坑汇总const 必须声明的时候直接赋值不能等到构造函数赋值readonly 可以在构造函数赋值。const int a; //编译报错const必须初始化const 不能放 new 对象不能放 DateTime.Now 这类运行时值这类只能用 static readonly。const 默认 static不需要 new 对象访问实例 readonly 必须 new 对象。const 是编译期文本替换跨项目引用时修改 const别的项目不重新编译不会生效。readonly 只是字段本身不能重新赋值如果 readonly 里面存的是引用类型对象对象内部属性仍然可以修改非常经典大坑大坑示例readonly 引用类型class Person { public string Name; } class Test { public readonly Person P new Person(); } static void Main() { Test t new Test(); // t.P new Person(); //报错不能修改P这个字段不能换对象 t.P.Name 张三; //✅合法对象里面的数据可以修改 }机理readonly 限制的是变量 / 字段保存的地址不能替换不是锁定堆里面对象内容。 想要对象内部也不可修改需要类内部自己做只读属性。