C# 中 const 和 readonly 關(guān)鍵字的區(qū)別和用法
前言
今天我們一起來(lái)講講 C# 中 const 和 readonly 關(guān)鍵字的區(qū)別和用法。
const 和 readonly 關(guān)鍵字區(qū)別
基本介紹
- const(常量): 在C#中用于聲明編譯時(shí)常量,其值在編譯時(shí)就必須確定,并且在程序生命周期內(nèi)不可更改。
- readonly(只讀字段): 在C#中用于聲明運(yùn)行時(shí)常量,其值可以在聲明時(shí)或構(gòu)造函數(shù)中初始化,之后不可更改(可通過(guò)反射強(qiáng)制修改)。
const 和 readonly 異同點(diǎn)
對(duì)比維度 | const | readonly |
基礎(chǔ)定義 | 編譯時(shí)常量,值在編譯期確定 | 運(yùn)行時(shí)常量,值在運(yùn)行時(shí)確定 |
初始化時(shí)機(jī) | 必須在聲明時(shí)初始化 | 可在聲明時(shí)或構(gòu)造函數(shù)中初始化 |
支持的數(shù)據(jù)類型 | 僅支持基元類型(int, float, char, bool等)、string和null引用 | 支持所有類型(包括自定義類和結(jié)構(gòu)體) |
默認(rèn)值要求 | 必須顯式初始化 | 不需要顯示初始化,值類型默認(rèn)零值,引用類型默認(rèn)null |
性能表現(xiàn) | 零開(kāi)銷訪問(wèn)(直接編譯到IL) | 微小訪問(wèn)開(kāi)銷(作為實(shí)例/靜態(tài)字段分配內(nèi)存) |
線程安全 | 天然線程安全 | 實(shí)例字段需注意可見(jiàn)性,靜態(tài)字段線程安全 |
反射修改 | 無(wú)法通過(guò)反射修改 | 可通過(guò)反射強(qiáng)制修改 |
IL元數(shù)據(jù)標(biāo)記 | literal 標(biāo)記 | initonly 標(biāo)記 |
使用場(chǎng)景 | 聲明常量字段或本地常量,常量可以是數(shù)字、布爾值、字符串或 null 引用等 | 聲明依賴注入對(duì)象、配置值、運(yùn)行時(shí)計(jì)算值等 |
const 和 readonly 關(guān)鍵字使用
const 使用
public enum UserRole
{
Admin,
User,
Guest
}
public class ConstAndReadonlyExercise
{
// const 初始化
public const int MaxCount = 999;
public const UserRole CurrentUserRole = UserRole.Admin;
}編譯后 IL 代碼:
- literal 關(guān)鍵字:標(biāo)記為字面量,值直接嵌入調(diào)用處的 IL。
.field public static literal int32 MaxCount = int32(999) // 0x000003e7
.field public static literal valuetype 'HelloDotNetGuide.CSharp語(yǔ)法.UserRole' CurrentUserRole = int32(0) // 0x00000000readonly 使用
// 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 關(guān)鍵字:標(biāo)志被 CLR 識(shí)別為
僅構(gòu)造函數(shù)可寫(xiě)約束。
.field public initonly string _applicationName
.field private static class 'HelloDotNetGuide.CSharp語(yǔ)法.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語(yǔ)法.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語(yǔ)法.ConstAndReadonlyExercise'::_applicationName
// [32 9 - 32 10]
IL_001e: ret
} // end of method ConstAndReadonlyExercise::.ctor
.method public hidebysig static specialname class 'HelloDotNetGuide.CSharp語(yǔ)法.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語(yǔ)法.ConstAndReadonlyExercise' V_3
)
// [37 13 - 37 14]
IL_0000: nop
// [38 17 - 38 39]
IL_0001: ldsfld class 'HelloDotNetGuide.CSharp語(yǔ)法.ConstAndReadonlyExercise''HelloDotNetGuide.CSharp語(yǔ)法.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語(yǔ)法.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語(yǔ)法.ConstAndReadonlyExercise''HelloDotNetGuide.CSharp語(yǔ)法.ConstAndReadonlyExercise'::_instance
IL_0025: brtrue.s IL_0031
IL_0027: newobj instance void 'HelloDotNetGuide.CSharp語(yǔ)法.ConstAndReadonlyExercise'::.ctor()
IL_002c: stsfld class 'HelloDotNetGuide.CSharp語(yǔ)法.ConstAndReadonlyExercise''HelloDotNetGuide.CSharp語(yǔ)法.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語(yǔ)法.ConstAndReadonlyExercise''HelloDotNetGuide.CSharp語(yǔ)法.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語(yǔ)法.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語(yǔ)法.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語(yǔ)法.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語(yǔ)法.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

























