《CLR Via C# 第3版》筆記之(七) - const和readonly
C#中經常用const或者readonly來定義不可改變常量,那么如何使用它們呢?
主要內容:
- const和readonly的區別
- readonly的補充說明
1. const和readonly的區別
主要的區別在于 const是在編譯時確定值的,readonly是在運行時確定值的。
因此,用const修飾的字段,必須在定義的時候就賦值,否則編譯器報錯。
而readonly修飾的字段除了可以在定義時賦值以外,還可以在構造函數中賦值。
驗證的代碼如下:
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代碼如下:
.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的字段可以通過反射來修改
具體參見以下代碼
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的字段為引用類型時,不可改變的是引用,而不是引用的對象。
驗證代碼如下:
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那樣是絕對的常量。我們在利用其靈活性的同時,也應注意它可能帶來的副作用。


浙公網安備 33010602011771號