《CLR Via C# 第3版》笔记之(七) - const和readonly
C#中经常用const或者readonly来定义不可改变常量,那么如何使用它们呢?
主要内容:
- const和readonly的区别
- readonly的补充说明
1. const和readonly的区别
主要的区别在于 const是在编译时确定值的,readonly是在运行时确定值的。
因此,用const修饰的字段,必须在定义的时候就赋值,否则编译器报错。
而readonly修饰的字段除了可以在定义时赋值以外,还可以在构造函数中赋值。
验证的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | using System; namespace Test7 { public class CLRviaCSharp_7 { const string cValue = "const" ; readonly string rValue; public CLRviaCSharp_7() { rValue = "readonly" ; } static void Main( string [] args) { CLRviaCSharp_7 test7 = new CLRviaCSharp_7(); Console.WriteLine( "cValue=" + CLRviaCSharp_7.cValue); Console.WriteLine( "rValue=" + test7.rValue); Console.ReadKey( true ); } } } |
编译后用ILSpy查看IL代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | .class public auto ansi beforefieldinit CLRviaCSharp_7 extends object { // Fields .field private static literal string cValue = "const" .field private initonly string rValue // Methods .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x20c7 // Code size 21 (0x15) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void object::.ctor() IL_0006: nop IL_0007: nop IL_0008: ldarg.0 IL_0009: ldstr "readonly" IL_000e: stfld string class Test7.CLRviaCSharp_7::rValue IL_0013: nop IL_0014: ret } // End of method CLRviaCSharp_7..ctor .method private static hidebysig void Main ( string[] args ) cil managed { // Method begins at RVA 0x20e0 // Code size 48 (0x30) .maxstack 2 .entrypoint .locals init ( [0] class Test7.CLRviaCSharp_7 test7 ) IL_0000: nop IL_0001: newobj instance void Test7.CLRviaCSharp_7::.ctor() IL_0006: stloc.0 IL_0007: ldstr "cValue=const" IL_000c: call void [mscorlib]System.Console::WriteLine(string) IL_0011: nop IL_0012: ldstr "rValue=" IL_0017: ldloc.0 IL_0018: ldfld string class Test7.CLRviaCSharp_7::rValue IL_001d: call string string::Concat(string, string) IL_0022: call void [mscorlib]System.Console::WriteLine(string) IL_0027: nop IL_0028: ldc.i4.1 IL_0029: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey(bool) IL_002e: pop IL_002f: ret } // End of method CLRviaCSharp_7.Main } // End of class Test7.CLRviaCSharp_7 |
从Main函数中的IL_0007,我们可以看出,编译时就已经将 cValue替换为字符串"const"了,所以在Main函数中看不出使用了字段cValue
从Main函数中的IL_0018,我们可以看出,运行时才读取 rValue的值,将其拼接到输出的字符串中。
2. readonly的补充说明
const和readonly虽然都可以定义常量,但是由于readonly是在运行时才确定值的,所以比const更加灵活。
当然,readonly的性能肯定比const稍逊。(具体相差多少还没有试验过)
readonly与const相比,使用时需要注意2点。
2.1. readonly的字段可以通过反射来修改
具体参见以下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | using System; using System.Reflection; namespace Test7 { public class CLRviaCSharp_7 { static void Main( string [] args) { ChangeReadonlyClass cr = new ChangeReadonlyClass(); Console.WriteLine( "before change, rValue=" + cr.rValue); // 利用反射来改变ChangeReadonlyClass中readonly字段的值 FieldInfo fi = typeof (ChangeReadonlyClass).GetField( "rValue" ); fi.SetValue(cr, "rValue has changed" ); Console.WriteLine( "after change, rValue=" + cr.rValue); Console.ReadKey( true ); } } public class ChangeReadonlyClass { public readonly string rValue; public ChangeReadonlyClass() { rValue = "ChangeReadonlyClass's readonly field" ; } } } |
运行结果如下:
2.2. readonly的字段为引用类型时,不可改变的是引用,而不是引用的对象。
验证代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | using System; namespace Test7 { public class CLRviaCSharp_7 { public static readonly Char[] rValues = new Char[] { 'X' , 'Y' , 'Z' }; static void Main( string [] args) { // 修改引用的对象,可以成功修改并运行 rValues[0] = 'A' ; rValues[1] = 'B' ; rValues[2] = 'C' ; // 修改引用本身,无法编译 rValues = new Char[] { 'A' , 'B' , 'C' }; Console.ReadKey( true ); } } } |
readonly并不像const那样是绝对的常量。我们在利用其灵活性的同时,也应注意它可能带来的副作用。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战