C# 中 const 和 readonly 关键字的区别和用法

前言
今天我们一起来讲讲 C# 中 const 和 readonly 关键字的区别和用法。
const 和 readonly 关键字区别
基本介绍
- const(常量): 在C#中用于声明编译时常量,其值在编译时就必须确定,并且在程序生命周期内不可更改。
- readonly(只读字段): 在C#中用于声明运行时常量,其值可以在声明时或构造函数中初始化,之后不可更改(可通过反射强制修改)。
const 和 readonly 异同点
| 对比维度 | const | readonly |
|---|---|---|
| 基础定义 | 编译时常量,值在编译期确定 | 运行时常量,值在运行时确定 |
| 初始化时机 | 必须在声明时初始化 | 可在声明时或构造函数中初始化 |
| 支持的数据类型 | 仅支持基元类型(int, float, char, bool等)、string和null引用 | 支持所有类型(包括自定义类和结构体) |
| 默认值要求 | 必须显式初始化 | 不需要显示初始化,值类型默认零值,引用类型默认null |
| 性能表现 | 零开销访问(直接编译到IL) | 微小访问开销(作为实例/静态字段分配内存) |
| 线程安全 | 天然线程安全 | 实例字段需注意可见性,静态字段线程安全 |
| 反射修改 | 无法通过反射修改 | 可通过反射强制修改 |
| IL元数据标记 | literal 标记 | initonly 标记 |
| 使用场景 | 声明常量字段或本地常量,常量可以是数字、布尔值、字符串或 null 引用等 | 声明依赖注入对象、配置值、运行时计算值等 |
const 和 readonly 关键字使用
const 使用
public enum UserRole
{
Admin,
User,
Guest
}
public class ConstAndReadonlyExercise
{
// const 初始化
public const int MaxCount = 999;
public const UserRole CurrentUserRole = UserRole.Admin;
}
编译后 IL 代码:
- literal 关键字:标记为字面量,值直接嵌入调用处的 IL。
.field public static literal int32 MaxCount = int32(999) // 0x000003e7
.field public static literal valuetype 'HelloDotNetGuide.CSharp语法.UserRole' CurrentUserRole = int32(0) // 0x00000000
readonly 使用
// readonly 初始化
public readonly string _applicationName = "HelloDotNetGuide";
public ConstAndReadonlyExercise()
{
_applicationName = "HelloDotNetGuide_V2";
}
// 懒汉式单例模式示例
private static ConstAndReadonlyExercise? _instance;
private static readonly object _lockObj = new object();
public static ConstAndReadonlyExercise Instance
{
get
{
if (_instance == null)
{
lock (_lockObj)
{
_instance ??= new ConstAndReadonlyExercise();
}
}
return _instance;
}
}
/// <summary>
/// 反射修改 readonly 字段的值
/// </summary>
public static void UpdateApplicationNameValue()
{
var instance = new ConstAndReadonlyExercise();
Console.WriteLine($"初始值: {instance._applicationName}");
// 输出: 初始值: HelloDotNetGuide_V2
var field = instance.GetType().GetField("_applicationName");
field.SetValue(instance, "HelloDotNetGuide_V3");
Console.WriteLine($"修改后: {instance._applicationName}");
// 输出: 修改后: HelloDotNetGuide_V3
}
编译后 IL 代码:
- initonly 关键字:标志被 CLR 识别为
仅构造函数可写约束。
.field public initonly string _applicationName
.field private static class 'HelloDotNetGuide.CSharp语法.ConstAndReadonlyExercise' _instance
.custom instance void [System.Runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(unsigned int8)
= (01 00 02 00 00 ) // .....
// unsigned int8(2) // 0x02
.field private static initonly object _lockObj
.method public hidebysig specialname rtspecialname instance void
.ctor() cil managed
{
.maxstack 8
// [25 9 - 25 70]
IL_0000: ldarg.0 // this
IL_0001: ldstr "HelloDotNetGuide"
IL_0006: stfld string 'HelloDotNetGuide.CSharp语法.ConstAndReadonlyExercise'::_applicationName
// [29 9 - 29 42]
IL_000b: ldarg.0 // this
IL_000c: call instance void [System.Runtime]System.Object::.ctor()
IL_0011: nop
// [30 9 - 30 10]
IL_0012: nop
// [31 13 - 31 54]
IL_0013: ldarg.0 // this
IL_0014: ldstr "HelloDotNetGuide_V2"
IL_0019: stfld string 'HelloDotNetGuide.CSharp语法.ConstAndReadonlyExercise'::_applicationName
// [32 9 - 32 10]
IL_001e: ret
} // end of method ConstAndReadonlyExercise::.ctor
.method public hidebysig static specialname class 'HelloDotNetGuide.CSharp语法.ConstAndReadonlyExercise'
get_Instance() cil managed
{
.maxstack 2
.locals init (
[0] bool V_0,
[1] object V_1,
[2] bool V_2,
[3] class 'HelloDotNetGuide.CSharp语法.ConstAndReadonlyExercise' V_3
)
// [37 13 - 37 14]
IL_0000: nop
// [38 17 - 38 39]
IL_0001: ldsfld class 'HelloDotNetGuide.CSharp语法.ConstAndReadonlyExercise''HelloDotNetGuide.CSharp语法.ConstAndReadonlyExercise'::_instance
IL_0006: ldnull
IL_0007: ceq
IL_0009: stloc.0 // V_0
IL_000a: ldloc.0 // V_0
IL_000b: brfalse.s IL_0040
// [39 17 - 39 18]
IL_000d: nop
// [40 21 - 40 36]
IL_000e: ldsfld object 'HelloDotNetGuide.CSharp语法.ConstAndReadonlyExercise'::_lockObj
IL_0013: stloc.1 // V_1
IL_0014: ldc.i4.0
IL_0015: stloc.2 // V_2
.try
{
IL_0016: ldloc.1 // V_1
IL_0017: ldloca.s V_2
IL_0019: call void [System.Threading]System.Threading.Monitor::Enter(object, bool&)
IL_001e: nop
// [41 21 - 41 22]
IL_001f: nop
// [42 25 - 42 70]
IL_0020: ldsfld class 'HelloDotNetGuide.CSharp语法.ConstAndReadonlyExercise''HelloDotNetGuide.CSharp语法.ConstAndReadonlyExercise'::_instance
IL_0025: brtrue.s IL_0031
IL_0027: newobj instance void 'HelloDotNetGuide.CSharp语法.ConstAndReadonlyExercise'::.ctor()
IL_002c: stsfld class 'HelloDotNetGuide.CSharp语法.ConstAndReadonlyExercise''HelloDotNetGuide.CSharp语法.ConstAndReadonlyExercise'::_instance
// [43 21 - 43 22]
IL_0031: nop
IL_0032: leave.s IL_003f
} // end of .try
finally
{
IL_0034: ldloc.2 // V_2
IL_0035: brfalse.s IL_003e
IL_0037: ldloc.1 // V_1
IL_0038: call void [System.Threading]System.Threading.Monitor::Exit(object)
IL_003d: nop
IL_003e: endfinally
} // end of finally
// [44 17 - 44 18]
IL_003f: nop
// [45 17 - 45 34]
IL_0040: ldsfld class 'HelloDotNetGuide.CSharp语法.ConstAndReadonlyExercise''HelloDotNetGuide.CSharp语法.ConstAndReadonlyExercise'::_instance
IL_0045: stloc.3 // V_3
IL_0046: br.s IL_0048
// [46 13 - 46 14]
IL_0048: ldloc.3 // V_3
IL_0049: ret
} // end of method ConstAndReadonlyExercise::get_Instance
.method public hidebysig static void
UpdateApplicationNameValue() cil managed
{
.maxstack 3
.locals init (
[0] class 'HelloDotNetGuide.CSharp语法.ConstAndReadonlyExercise''instance',
[1] class [System.Runtime]System.Reflection.FieldInfo 'field'
)
// [50 9 - 50 10]
IL_0000: nop
// [51 13 - 51 59]
IL_0001: newobj instance void 'HelloDotNetGuide.CSharp语法.ConstAndReadonlyExercise'::.ctor()
IL_0006: stloc.0 // 'instance'
// [52 13 - 52 68]
IL_0007: ldstr "初始值: "
IL_000c: ldloc.0 // 'instance'
IL_000d: ldfld string 'HelloDotNetGuide.CSharp语法.ConstAndReadonlyExercise'::_applicationName
IL_0012: call string [System.Runtime]System.String::Concat(string, string)
IL_0017: call void [System.Console]System.Console::WriteLine(string)
IL_001c: nop
// [55 13 - 55 73]
IL_001d: ldloc.0 // 'instance'
IL_001e: callvirt instance class [System.Runtime]System.Type [System.Runtime]System.Object::GetType()
IL_0023: ldstr "_applicationName"
IL_0028: callvirt instance class [System.Runtime]System.Reflection.FieldInfo [System.Runtime]System.Type::GetField(string)
IL_002d: stloc.1 // 'field'
// [56 13 - 56 61]
IL_002e: ldloc.1 // 'field'
IL_002f: ldloc.0 // 'instance'
IL_0030: ldstr "HelloDotNetGuide_V3"
IL_0035: callvirt instance void [System.Runtime]System.Reflection.FieldInfo::SetValue(object, object)
IL_003a: nop
// [58 13 - 58 68]
IL_003b: ldstr "修改后: "
IL_0040: ldloc.0 // 'instance'
IL_0041: ldfld string 'HelloDotNetGuide.CSharp语法.ConstAndReadonlyExercise'::_applicationName
IL_0046: call string [System.Runtime]System.String::Concat(string, string)
IL_004b: call void [System.Console]System.Console::WriteLine(string)
IL_0050: nop
// [60 9 - 60 10]
IL_0051: ret
} // end of method ConstAndReadonlyExercise::UpdateApplicationNameValue
C#/.NET/.NET Core面试宝典
本文已收录至C#/.NET/.NET Core面试宝典中,更多C#关键字详解请前往C#/.NET/.NET Core面试宝典开放地址查阅。


C# 中 const 和 readonly 关键字的区别和用法的更多相关文章
- C#中 const 和 readonly 的区别
C#中 const 和 readonly 的区别 来源 https://www.cnblogs.com/gsk99/archive/2008/10/10/1308299.html http://dev ...
- C#基础知识七之const和readonly关键字
前言 不知道大家对const和readonly关键字两者的区别了解多少,如果你也不是很清楚的话,那就一起来探讨吧!探讨之前我们先来了解静态常量和动态常量. 静态常量 所谓静态常量就是在编译期间会对变量 ...
- JavaScript中const、var和let区别浅析
在JavaScript中有三种声明变量的方式:var.let.const.下文给大家介绍js中三种定义变量的方式const, var, let的区别. 1.const定义的变量不可以修改,而且必须初始 ...
- 实例讲述PHP面向对象的特性;;;php中const与define的使用区别
php中const与define的使用区别 1.const:类成员变量定义,一旦定义且不能改变其值. define:定义全局常量,在任何地方都可以访问.2.define:不能在类中定义,而const可 ...
- 数据库中in和exists关键字的区别
数据库中in和exists关键字的区别 in 是把外表和内表作hash join,而exists是对外表作loop,每次loop再对内表进行查询. 一直以来认为exists比in效率高的说法是不准确的 ...
- nginx中root与alias关键字的区别
前言 近段时间秋招上岸了,于是每天疯狂补各种分布式基础,每天都在痛苦与快乐中度过. 在学习 nginx 的时候,遇到配置上的问题:root 与 alias 的区别,卡了大概三个小时,记录下来警醒自己不 ...
- MVC+Spring.NET+NHibernate .NET SSH框架整合 C# 委托异步 和 async /await 两种实现的异步 如何消除点击按钮时周围出现的白线? Linq中 AsQueryable(), AsEnumerable()和ToList()的区别和用法
MVC+Spring.NET+NHibernate .NET SSH框架整合 在JAVA中,SSH框架可谓是无人不晓,就和.NET中的MVC框架一样普及.作为一个初学者,可以感受到.NET出了MV ...
- Linq中 AsQueryable(), AsEnumerable()和ToList()的区别和用法
Linq中 AsQueryable(), AsEnumerable()和ToList()的区别和用法:在写LINQ语句的时候,往往会看到AsEnumerable() ,AsQueryable() 和T ...
- C#中Const和Readonly的区别
const 的概念就是一个包含不能修改的值的变量.常数表达式是在编译时可被完全计算的表达式.因此不能从一个变量中提取的值来初始化常量.如果 const int a = b+1;b是一个变量,显然不能再 ...
- c#中const与readonly区别
const 的概念就是一个包含不能修改的值的变量.常数表达式是在编译时可被完全计算的表达式.因此不能从一个变量中提取的值来初始化常量.如果 const int a = b+1;b是一个变量,显然不能再 ...
随机推荐
- 探秘Transformer系列之(22)--- LoRA
探秘Transformer系列之(22)--- LoRA 目录 探秘Transformer系列之(22)--- LoRA 0x00 概述 0x01 背景知识 1.1 微调 1.2 PEFT 1.3 秩 ...
- freertos消息队列的值传递和指针传递
消息队列的使用方法总结: 1.消息队列初始化(定义一个消息队列的结构体),一般在main.c中完成. 2.消息队列的发送: a extern 消息队列 b 定义一个结构体的指针指向消息消息队列 ...
- 🎀B站-网页优化插件BewlyBewly
简介 一个开源的B站网页优化浏览器插件,对B站网页进行了调整和优化,页面更具视觉吸引力和用户友好性. 源码 https://github.com/BewlyBewly/BewlyBewly 支持 插件 ...
- [not]exists和[not]in的区别
前言:近期在处理业务实际问题时发现使用in导致查询速度非常慢,于是查阅资料后发现使用exists能极大缩短查询时间,于是便将此经历记录下来. 数据源: grade表 stu_info表 exists与 ...
- github项目收集
web模块 Nginx 监控模块vts: https://github.com/vozlt/nginx-module-vts
- React项目报错:Element type is invalid: expected a string可能的原因
React项目报错:Element type is invalid: expected a string 起因:React使用Antd组件库,因为某些原因实在用不下去了,代码不变直接改成Tdesig ...
- AutoFac学习Demo1——官网Demo
AutoFac实现Demo1 1.创建一个工作台(.NET Core)程序AutofacDemo1,nuget引入AutoFac,搜索第一个就是 2.创建输出接口IOutput及实现ConsoleOu ...
- 操作系统综合题之“按要求是个进程协调完成任务,补充完整下列程序,将编号①~⑩处空缺的内容填写(Buffer缓冲区问题-代码补充)”
1.问题:假设某系统有四个进程.input1和input2进程负责从不同设备读取数据,分别表示为data1和data2,存放在缓冲区Buffer中,output1和output2进程负责从Buffer ...
- Flutter开发的高效图片压缩工具:让APP更加丝滑
@charset "UTF-8"; .markdown-body { line-height: 1.75; font-weight: 400; font-size: 15px; o ...
- FMEA方法,排除架构可用性隐患的利器
极客时间:<从 0 开始学架构>:FMEA方法,排除架构可用性隐患的利器 FMEA 方法,就是保证我们做到全面分析的一个非常简单但是非常有效的方法. 1.FMEA 介绍 FMEA(Fail ...