十八:常量和字段
常量就是定义完以后就永远不会再改变的符号,它的值是在编译时必须确定的,编译通过后,编译器将常量的值保存在程序集的元数据中,定义常量将导致元数据的产生,常量只能是基元类型。
因为常量的值不会被改变,所以可以说常量是类型的一部分,而不是实例成员的,是静态的。
当代码引用一个常量符号时,编译器将在定义常量的程序集的元数据中查找该符号,提取常量的值,并将值嵌入到编译后生成的IL代码中。因为常量的值在编译时就直接嵌入代码,所以运行时就不需要为常量分配任何内存,也不能得到常量的地址,不能通过引用来传递常量。
这些特征说明常量没有很好的跨程序集版本控制特征,所以常量只能用于确定某符号的值永远不会改变时。
看以下代码:
先在VS2005中创建一个解决方案,添加一个类库,在该增加下面这个类型SomeLibraryType.cs
using System;
public sealed class SomeLibraryType
{
public const Int32 TestConst = 100;
}
在添加一个控制台应用程序,该项目引用上面创建的类库,在该项目里添加这个类型Program.cs
using System;
public sealed class Program
{
public static void Main()
{
Console.WriteLine("The const is : " + SomeLibraryType.TestConst);
}
}
注意,Program.cs程序代码中引用的常量TestConst是在SomeLibraryType类中定义的,当编译器构建程序代码时,它知道TestConst是个常量符号,其值为100,并将该值嵌入到应用程序的IL代码中,如下:
当应用程序构建以后,就不需要在敌对行动时加载TextConst所在的类库的DLL程序集了,如果将TestConst改为200,并重新单独构建DLL程序集或是应用程序集该常量值都不会得到影响,只有重新全部都进行编译。
以上可以看出使用常量带来的版本控制的问题。
字段是一种数据成员,存放着值类型的实例或者引用类型的引用。
CLR支持静态字段(类型的)和非静态字段(实例的),对于类型字段,存放字段数据所需要的动态内存是在类型对象内部分配的,而动态内存是在类型加载到应用程序域时创建的,也就是说在首次引用类型时才创建的动态内存,就是JIT编译,对于实例字段,创建类型实例时才分配存放字段的动态内存。因为字段是存储在动态内存中的,所以只有在运行时才可以得到字段的值。字段可以解决常量存在的版本控制问题。字段可以是任何数据类型,不像常量那样只有基元类型。
字段还有只读字段和读写字段,读写字段说明在执行代码过程中可以多次改变字段的值,但是只读字段只能在构造器方法中写入数值,称之为一次写,编译器和验证机制确保只读字段不能被构造器以外的任何其它方法写入。
利用上面常量的程序,把类库中的程序改成用只读字段来实现:
using System;
public sealed class SomeLibraryType
{
public static readonly Int32 TestConst = 100;
}
在程序编译完执行后,假设我们修改一个TestConst的值为200,重新生成类库的程序集,当应用程序代码重新执行时,他会自动去提取新值200,应用程序集不需要重新编译。查看应用程序集的IL代码,可以看到没有像常量一样的里面嵌入值100。