C#中有两种常量类型,分别为readonly(运行时常量)与const(编译时常量),本文将就这两种类型的不同特性进行比较并说明各自的适用场景。 工作原理 readonly为运行时常量,程序运行时进行赋值,赋值完成后便无法更改,因此也有人称其为只读变量。 const为编译时常量,程序编译时将对常量值进行解析,并将所有常量引用替换为相应值。 下面声明两个常量:
public static readonly int A = 2; // A为运行时常量 publicconst int B = 3; // B为编译时常量
下面的表达式:
int C =A + B;
经过编译后与下面的形式等价:
int C =A + 3 ;
可以看到,其中的const常量B被替换成字面量3,而readonly常量A则保持引用方式。 声明及初始化 readonly常量只能声明为类字段,支持实例类型或静态类型,可以在声明的同时初始化或者在构造函数中进行初始化,初始化完成后便无法更改。 const常量除了可以声明为类字段之外,还可以声明为方法中的局部常量,默认为静态类型(无需用static修饰,否则将导致编译错误),但必须在声明的同时完成初始化。 数据类型支持 由于const常量在编译时将被替换为字面量,使得其取值类型受到了一定限制。const常量只能被赋予数字(整数、浮点数)、字符串以及枚举类型。下面的代码无法通过编译:
public const DateTime D = DateTime.MinValue;
改成readonly就可以正常编译:
public readonlyDateTime D =DateTime.MinValue;
可维护性 readonly以引用方式进行工作,某个常量更新后,所有引用该常量的地方均能得到更新后的值。 const的情况要稍稍复杂些,特别是跨程序集调用:
public class Class1   { public static readonly int A = 2; // A为运行时常量 public const int B =3 ; //B为编译时常量 }  publicclass Class2   { public static int C =Class1.A + Class1.B; // 变量C的值为A、B之和 }  Console.WriteLine(Class2.C);// 输出"5"
假设Class1与Class2位于两个不同的程序集,现在更改Class1中的常量值:
public class Class1   { public static readonly int A = 4; // A为运行时常量 public const int B =5 ; //B为编译时常量 }
编译Class1并部署(注意:这时并没有重新编译Class2),再次查看变量C的值:
Console.WriteLine(Class2.C); //输出"7"
结果可能有点出乎意料,让我们来仔细观察变量C的赋值表达式:
public static int C =Class1.A + Class1.B;
编译后与下面的形式等价:
public static int C =Class1.A + 3 ;
因此不管常量B的值如何变,对最终结果都不会产生影响。虽说重新编译Class2即可解决这个问题,但至少让我们看到了const可能带来的维护问题。 性能比较 const直接以字面量形式参与运算,性能要略高于readonly,但对于一般应用而言,这种性能上的差别可以说是微乎其微。 补充readonly与const的区别: C#的readonly关键字只能在字段上面使用 public readonly TcpClient client; 不能在类,方法,属性上面使用readonly!! 顺便看了一下readonly和const的区别:
- readonly和const都是用来标识常量的。
- const可用于修饰class的field或者一个局部变量(local variable);而readonly仅仅用于修饰class的field。
- const常量的值必定在编译时就已明确并且恒定的;而readonly常量却有一点不同,那就是其值可以在运行时编译,当然,它也必须遵守作为常量的约束,那就是值必须恒定不变。
- const常量必须在声明的同时对其进行赋值,并且确保该值在编译时可确定并恒定;而readonly常量则可以根据情况选择在声明的同时对其赋予一个编译时确定并恒定的值,或者将其值的初始化工作交给实例构造函数(instant constructor)完成。如:public readonly string m_Now = DateTime.Now.ToString();,m_Now会随着运行时实际情况变化而变化。
- const常量属于类级别(class level)而不是实例对象级别(instant object level),并且它不能跟static结合一起使用,该常量的值将由整个类的所有实例对象共同分享(详细论述参见后面的Remark区域)。
- readonly常量既可以是类级别也可以是实例对象级别的,这取决于它的声明以及初始化工作怎么实施。readonly可以与static结合使用,用于指定该常量属于类级别,并且把初始化工作交由静态构造函数(static constructor)完成(有关如何把readonly常量声明为类级别或实例对象级别的论述清参见后面的Remark区域) 。
- 能被const修饰声明为常量的类型必须是以下的基元类型(primitive type):sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, float, bool, decimal, string。
- object, 数组(Array)和结构(struct)不能被声明为const常量。
- 一般情况下,引用类型是不能被声明为const常量的,不过有一个例外:string。该引用类型const常量的值可以有两种情况,string或null。其实,string虽然是引用类型,但是.NET却对它特别处理,这种处理叫做字符串恒定性(immutable),使得string的值具有只读特性。有关字符串恒定性的内容,可以参考《Microsoft .NET框架程序设计(修订版)》。
|