个人认为两者都可以认为是常量,但是却又是有区别的。
首先看看两者所支持的类型:
const 只支持基元类型(所谓基元类型是指编译器直接支持的类型,也就是像int,char,string等等在C#编译器中定义了关键字的类型)。
readonly 可以支持所有类型。
然后再看看两者所限定常量的本质:
const 限定的是变量的引用以及变量所代表的值。
readonly仅仅限定的是变量的引用。(如果是引用类型的话,变量所代表的值是能够改变的)
最后看看编译器对这两者进行的处理:
const
当编译器遇到一个对const对象的引用时,编译器会将const变量所代表的值直接嵌入生成的IL代码中,也就是说编译完后这段代码和所引用的const变量就没有任何关系了。
这样的话如果我们在代码中修改了const变量的值然后只编译const变量所在程序集的话是不行的,必须为所有引用该变量的代码重新生成IL。
readonly
对于readonly的处理,编译器和对待其它的变量一样。因此编译后,调用代码对readonly的变量仍然是存在依赖的。在运行时,调用代码才会去访问readonly
变量所代表的对象的值。
写个小程序,生成IL验证一下:
public
class Constants
{
public const string ConstName = " Jensen ";
public static readonly int guard = 2;
}
class Program
{
static void Main( string[] args)
{
Console.WriteLine(Constants.ConstName);
Console.WriteLine(Constants.guard);
Console.ReadKey();
}
}
{
public const string ConstName = " Jensen ";
public static readonly int guard = 2;
}
class Program
{
static void Main( string[] args)
{
Console.WriteLine(Constants.ConstName);
Console.WriteLine(Constants.guard);
Console.ReadKey();
}
}
让我们来瞅瞅生成的IL代码:
.method private hidebysig static void Main(string
[] args) cil managed
{
.entrypoint
// Code size 30 (0x1e)
.maxstack 8
IL_0000: nop
IL_0001: ldstr "Jensen" --由于此处是对const变量的引用,编译器直接将它的值写入了IL代码中。
IL_0006: call void [ mscorlib ]System.Console::WriteLine(string)
IL_000b: nop
IL_000c: ldsfld int32 [ Entity ]Entity.Constants::guard --此处是对readonly变量的引用,编译依然是保存了对guard变量运行时的依赖。
IL_0011: call void [ mscorlib ]System.Console::WriteLine(int32)
IL_0016: nop
IL_0017: call valuetype [ mscorlib ]System.ConsoleKeyInfo [ mscorlib ]System.Console::ReadKey()
IL_001c: pop
IL_001d: ret
} // end of method Program::Main
{
.entrypoint
// Code size 30 (0x1e)
.maxstack 8
IL_0000: nop
IL_0001: ldstr "Jensen" --由于此处是对const变量的引用,编译器直接将它的值写入了IL代码中。
IL_0006: call void [ mscorlib ]System.Console::WriteLine(string)
IL_000b: nop
IL_000c: ldsfld int32 [ Entity ]Entity.Constants::guard --此处是对readonly变量的引用,编译依然是保存了对guard变量运行时的依赖。
IL_0011: call void [ mscorlib ]System.Console::WriteLine(int32)
IL_0016: nop
IL_0017: call valuetype [ mscorlib ]System.ConsoleKeyInfo [ mscorlib ]System.Console::ReadKey()
IL_001c: pop
IL_001d: ret
} // end of method Program::Main