定义

1
2
3
4
//编译时常量
public const int MaxSize = 10 * 10 * 1024;
//运行时常量
public static readonly int ThisYear = 2004;

编译时常量可以在方法体中声明,运行时常量不能在方法体中重声明

不同

1、编译时常量和运行时常量访问方式不同导致不同的行为。在目标代码中编译时常量会被替换成常量值。

比如下面代码:

1
2
3
4
// Compile time constant:
public const int Millennium = 2000;
// Runtime constant:
public static readonly int ThisYear = 2004;

会和下面写法的编译的 IL 代码是一样的:

1
if (myDateTime.Year == 2000)

运行时常量的值是在运行时得到的。当你引用一个只读(read-only)常量, IL 会引用一个 readonly 变量而不是直接使用值。

2、修饰的类型不同

​ 使用编译时常量和运行时常量还有不同的限制。编译时常量只能在基本类型(内建整数和浮点数类型),枚举类型,或字符串。编译时常量要求类能用有意义的常量赋值初始化。而只有基本类型才能在 IL 代码中使用常量(literal values)来替换。不能使用使用 new 操作法初始化编译时常量,即使它是一个值类型:

1
2
3
// Does not compile, use readonly instead:
private const DateTime classCreation = new
DateTime(2000, 1, 1, 0, 0, 0);

​ 编译时常量只能使用与数字和字符串。只读(Read-only)变量也是常量,即不能在构造函数完成之后再修改。但只读变量是在运行时赋值。这会比编译时常量更灵活。首先,运行时常量可以是任何类型。你必须在构造函数或者直接初始化。你可以让 DateTime 结构体变为 readonly 值;但不能使用 const 创建 DateTime 值。

3、确定值的时机不一样

​ 只读变量最重要的不同在于运行时才确定值。当你引用一个只读变量, IL 会为你产生一个指向只读变量的引用,而不是值。这种差异将对维护上产生深远的影响。编译时常量产生的 IL 代码就跟直接使用数值变量时一样的,即使是跨程序集:一个程序集的常量在另一个程序集还是被替换为数值。

换句话说,更改const定义的常量值,程序都需要重新编译

4、性能

已知的常量值会比使用变量访问的 readonly 变量产生稍微高效的代码。然而,性能上甚微的收效和灵活性的减小应该做一个很好的权衡。放弃灵活性之前一定要剖析性能差异。