首页 / 软件开发 / C# / Effective C#原则2:为你的常量选择readonly而不是const
Effective C#原则2:为你的常量选择readonly而不是const2010-12-09 博客园 Wu.Country@侠缘译对于常量,C#里有两个不同的版本:运行时常量和编译时常量。因为 他们有不同的表现行为,所以当你使用不当时,将会损伤程序性能或者出现错误 。两害相权取其轻,当我们不得不选择一个的时候,我们宁可选择一个 运行慢一点但正确的那一个,而不是运行快一点但有错误的那个。基于这个理由 ,你应该选择运行时常量而不是编译时常量(译注:这里隐藏的说明了编译时常 量效率更高,但可能会有错误)。编译时常量更快更直接,但在可维护性 上远不及运行时常量。保留编译时常量是为了满足那些对性能要求克刻,且随着 程序运行时间的过去,其值永远不发生改变的常量使用的(译注:这说明编译时 常量是可以不被C#采用的,但考虑到性能问题,还是做了保留)。你可以 用关键字readonly来声明(declare)一个运行时常量,编译时常量是用关键字 const声明的。//Compile time constant:
public cocnst int _Millennium = 2000;
//Runtime constant:
public static readonly int _ThisYear = 2007;//
(译注:原文为2004)编译时常量与运行时常量不同之处表现在如何对他们的访问上。一个编译时常量会被目标代码中的值直接取代。下面的代码:if (myDateTime.Year == _Millennium)
会与下面写的代码编译成完 全相同的IL代码:if(myDateTime.Year == 2000)
运行时常量的值是在运行时确定的。当你引用一个只读常量时(read-only)IL会 为你引用一个运行时常量的变量,而不是直接使用该值。当你任意的使 用其中一个常量时,这些区别就在一些限制上表现出来。编译时常量只能是基本 类型(primitive types)(built-in integral and floating-poing types),枚举 或者是字符串。这些就是你只能给运行时常量在初始化时赋值的类型。这些基本 类就是可以被编译器在编译IL代码时直接用真实的值所取代的数据类型。下面的 代码块(construct)不能通过编译。你不能用new运算符初始化一个编译时常量, 即使这个数据类型是值类型。//Does not complie, use readonly instead:private const DateTime _classCreation = new DateTime (2000,1,1,0,0,0);(译注:DateTime是一个值类型数据,但上面的代码 因为用了new运算符,编译器无法在编译确定具体的对象应该用什么样的实际值 来取代,所以无法通过编译。)编译时常量仅限于数字和字符串。只读变 量,也就是运行时常量,在构造函数(constructor)执行完成后它们是不以能被 修改的。但只读变量是所有不同的,因为他们是在运行时才赋值的。当你使用运 行时常量时,你有更大的可伸缩性。有一点要注意的是,运行时常量可以是任何 类型的数据。而且你必须在构造函数里对他们初始化,或者你可以用任何一个初 始化函数来完成。你可以添加一个DateTime结构的只读变量(--运行时常量),但 你不能添加一个DateTime结构的(编译时)常量。你可以把每一个实例(的 常量)指定为只读的,从而为每一个类的实例存放不同的值。与编译时常量不同 的是,它只能是静态的。(译注:简单的讲,运行时常量可以是一个类的 实例成员,也可以是一个类型的静态成员,而编译时常量只能是静态成员,因此 类似:static const string m_name;的代码是不能通过编译的。)只读 数据最重要的区别是他们在运行时才确定值。当你使用只读变量 时,IL会为你 产生一个对只读变量引用,而不是直接产生数值。随着时间的推移,这个区别在 (系统)维护上有深远的潜在影响。编译时常量生成的IL代码就跟直接使 用数值时生成的IL是一样的,即使是在跨程序集时:一个程序集里的编译时常量 在另一个程序集会保留着同样的值(译注:这里说的不是很清楚,看后面的这个 例子可能会更清楚一些)。编译时常量和运行时常量的赋值方法对运行时 的兼容性有所影响。假设你已经在程序集Infrastructure中同时定义了 一个const和一个readonly变量:public class UserfulValues {
public static readonly int StartValue = 5;
public const int EndValue = 10;
}