前言

今天我们一起来讲讲 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 关键字的区别和用法的更多相关文章

  1. C#中 const 和 readonly 的区别

    C#中 const 和 readonly 的区别 来源 https://www.cnblogs.com/gsk99/archive/2008/10/10/1308299.html http://dev ...

  2. C#基础知识七之const和readonly关键字

    前言 不知道大家对const和readonly关键字两者的区别了解多少,如果你也不是很清楚的话,那就一起来探讨吧!探讨之前我们先来了解静态常量和动态常量. 静态常量 所谓静态常量就是在编译期间会对变量 ...

  3. JavaScript中const、var和let区别浅析

    在JavaScript中有三种声明变量的方式:var.let.const.下文给大家介绍js中三种定义变量的方式const, var, let的区别. 1.const定义的变量不可以修改,而且必须初始 ...

  4. 实例讲述PHP面向对象的特性;;;php中const与define的使用区别

    php中const与define的使用区别 1.const:类成员变量定义,一旦定义且不能改变其值. define:定义全局常量,在任何地方都可以访问.2.define:不能在类中定义,而const可 ...

  5. 数据库中in和exists关键字的区别

    数据库中in和exists关键字的区别 in 是把外表和内表作hash join,而exists是对外表作loop,每次loop再对内表进行查询. 一直以来认为exists比in效率高的说法是不准确的 ...

  6. nginx中root与alias关键字的区别

    前言 近段时间秋招上岸了,于是每天疯狂补各种分布式基础,每天都在痛苦与快乐中度过. 在学习 nginx 的时候,遇到配置上的问题:root 与 alias 的区别,卡了大概三个小时,记录下来警醒自己不 ...

  7. 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 ...

  8. Linq中 AsQueryable(), AsEnumerable()和ToList()的区别和用法

    Linq中 AsQueryable(), AsEnumerable()和ToList()的区别和用法:在写LINQ语句的时候,往往会看到AsEnumerable() ,AsQueryable() 和T ...

  9. C#中Const和Readonly的区别

    const 的概念就是一个包含不能修改的值的变量.常数表达式是在编译时可被完全计算的表达式.因此不能从一个变量中提取的值来初始化常量.如果 const int a = b+1;b是一个变量,显然不能再 ...

  10. c#中const与readonly区别

    const 的概念就是一个包含不能修改的值的变量.常数表达式是在编译时可被完全计算的表达式.因此不能从一个变量中提取的值来初始化常量.如果 const int a = b+1;b是一个变量,显然不能再 ...

随机推荐

  1. 探秘Transformer系列之(22)--- LoRA

    探秘Transformer系列之(22)--- LoRA 目录 探秘Transformer系列之(22)--- LoRA 0x00 概述 0x01 背景知识 1.1 微调 1.2 PEFT 1.3 秩 ...

  2. 解决 Maven 打包项目中 Excel 文件乱码问题

    在 Java 项目开发过程中,我们常常会使用 Maven 来管理项目依赖和进行项目打包.当涉及到使用 Freemarker 导出 Excel 文件时,不少开发者可能会遇到一个让人头疼的问题 --Exc ...

  3. zk源码—5.请求的处理过程

    大纲 1.服务器的请求处理链 (1)Leader服务器的请求处理链 一.PrepRequestProcessor请求预处理器 二.ProposalRequestProcessor事务投票处理器 三.S ...

  4. python-pandas提取网页内tables(表格类型)数据

    比如,下面网页里大学排行的数据 分析这个页面,表格内的数据是包裹在tables里的 这样就可以使用pandas对数据进行提取并且导出成csv文件,具体代码很简单 import pandas as pd ...

  5. 康谋分享 | 自动驾驶联合仿真——功能模型接口FMI(终)

    在之前的文章中,我们介绍了如何构建简单的车辆模型,并基于FMI2.0构建了其FMU,其最终结构为: 今天将会和大家分享如何在aiSim中,通过UDP和aiSim车辆动力学API(Vehicle Dyn ...

  6. MySQL 的 Doublewrite Buffer 是什么?它有什么作用?

    MySQL 的 Doublewrite Buffer 是什么?它有什么作用? Doublewrite Buffer 是 InnoDB 存储引擎的一种机制,旨在提高数据的安全性,防止在写入磁盘时发生崩溃 ...

  7. 工良出品 | 长文讲解 MCP 和案例实战

    作者:痴者工良 博客地址:https://www.whuanle.cn/ 示例项目地址:https://github.com/whuanle/mcpdemo 近期 MCP 协议越来越爆火,很多开发者都 ...

  8. 智表ZCELL专业版授权说明

    专业版: 1.智表专业版按照部署地址授权,价格1680元.(IP或域名均可,授权时localhost会同步授权) 2.授权版本为智表专业版最新版本,不提供历史版本授权.授权为插件使用权,不提供源码. ...

  9. Ubuntu20.04 搭建Kubernetes 1.28版本集群

    环境依赖 以下操作,无特殊说明,所有节点都需要执行 安装 ssh 服务 安装 openssh-server sudo apt-get install openssh-server 修改配置文件 vim ...

  10. 交易信号---MACD、RSI、Boll、分型等技术信号

    技术指标 在交易决策过程中的简图: 什么是技术指标? 基于行情数据,通过特定数学公式或模型计算得出的.用于辅助交易决策的数值序列 技术指标的分类 三种关系: 趋势线: 股市走势震荡起伏,供需关系被打破 ...