C#中的常量分为两种类型:运行时常量和编译时常量,运行时常量使用readonly修饰,编译时常量使用const修饰。
运行时常量和编译时常量有以下区别:
- 运行时常量是在程序运行时才会进行解析,而编译时常量是在程序进行编译时,就进行解析了。
- 从生成的IL来看,运行时常量在IL中依然会指向你声明的变量;而编译时常量在IL中已经变为具体的值了。
- 编译时常量只能应用于基本类型,包括数字和字符串,对于复杂类型,编译时常量不可以通过new的方式进行初始化;而运行时常量可以应用到所有类型,可以对复杂类型通过new的方式进行初始化,但是当初始化结束后,就不可以再对常量的值进行修改了。
- 运行时常量是二进制兼容的,而编译时常量是二进制不兼容的。换句话说,当我们在一个程序集中声明常量,在另一个程序集中使用时,如果修改了常量的值,在不对引用常量的程序集进行重新编译的情况下,运行时常量得出的结果是正确的,是修改后的值;而编译时常量得出的结果是错误的,是修改前的值。
- 我们可以从另外一个角度来看待上述4,如果我们在一个程序集A中定义常量,在程序集B中引用常量,那么在程序集B中,看到的编译时常量就是一个具体的数值或者字符串;看到的运行时常量则是声明常量时使用的变量名称。如果我们把常量想象成一个对外的接口,那么修改编译时常量的值,可以认为是对接口的声明进行修改,这样调用接口的地方,必须进行重新编译;修改运行时常量的值,可以认为是对接口的实现进行修改,只要接口不变,那么调用接口的地方,就没有必要进行重新编译。
和运行时常量相比,编译时常量的一个优势就是性能,由于两者在IL中的不同,造就了编译时常量的性能要优于运行时常量。
基于上述的区别,我们在一般情况下,优先考虑运行时常量,只有在一些特殊情况下,才会考虑使用编译时常量:
- 能够确定常量不会随着时间的流逝而发生变化。
- 必须在编译时就要使用常量的值,例如枚举定义。
注意:一个运行速度慢、但是运行结果准确的程序,要比一个运行速度快,但是运行结果错误的程序,有价值得多。