编译时常量和运行时常量

 

C#有2种不同的常量定义方式:编译时常量(compile-time)和运行时常量(runtime).他们的行为方式有很大的不同,如果选用了错误的一个,那么将让程序的性能和正确性产生商榷.当然,一个系统最好没有任何问题,但是如果一定会存在一个问题,那么一个稍慢的但强壮的系统比一个速度更快的但很脆弱的系统要好.基于以上理由,在二者选其一的时候,你最好选择运行时常量.

运行时常量以 readonly 关键字定义,编译时是常量以 const 关键字定义:

public const int _Millennium = 2000;//编译时常量

public static readonly int _ThisYear = 2008;//运行时常量

编译时常量在源代码中就已经被替换成它的值.

if ( myDateTime.Year == _ ThisYear)

if ( myDateTime.Year == 2008 )

以上2句代码生成的IL是完全一样的.

运行时常量是在程序运行在被取值,运行时常量在产生IL时是产生该常量的引用,而不是它的值.

当你使用其中一种常量的时候,这点区别却产生出很多限制.编译时常量只允许用在几种原始数据类型上,比如整数浮点数,枚举或者字符串类型,只有这几种类型可以被正确的初始化,能在编译成IL时产生文本数据.编译时常量不能初始化一个用new操作符,甚至已经被赋值了一个具体的值的类型.

如以下代码并不能通过编译:

private const DateTime _classCreation = new DateTime( 2008, 1, 1, 0, 0, 0 );

而运行时常量可以被初始化成任何类型.

编译时常量和运行时常量的求值方式影响运行时的兼容性.假设你在一个名叫 Infrastructure 的程序集里面同时有编译时常量和运行时常量2种成员:

public class UsefulValues

{

 public static readonly int StartValue = 5;

 public const int EndValue = 10;

}

在另外一个程序集里面,你引用了他们的值,

for ( int i = UsefulValues.StartValue;i < UsefulValues.EndValue;i++ )

 Console.WriteLine( "value is {0}", i );

显然,得到的值应该如下:

Value is 5

Value is 6

...

Value is 9

一段时间以后,你发布了一个有如下改动的Infrastructure程序集的新版本,

public class UsefulValues

{

 public static readonly int StartValue = 105;

 public const int EndValue = 120;

}

在没有重新编译整个应用程序集的时候你分发了Infrastructure程序集,你期望能得到以下的值:

Value is 105

Value is 106

...

Value is 119

但事实上,你得不到任何输出.这个循环使用了105作为起始判断条件,而使用了10作为结束判断条件.

C#编译器将Const常量的值10代替了Endvalue的存储引用,而对于StartValue而言,它是通过readonly定义,是在运行时取值,因此应用程序在未编译整个程序集的前提下能获得其最新的值.查看上面那段代码生成IL我们能完整的看到运行过程:

IL_0000:  ldsfld     int32 Infras.UsefulValues::StartValue

IL_0005:  stloc.0

IL_0006:  br.s       IL_001c

IL_0008:  ldstr      "value is {0}"

IL_000d:  ldloc.0

IL_000e:  box        [mscorlib]System.Int32

IL_0013:  call       void [mscorlib]System.Console::WriteLine

(string,object)

IL_0018:  ldloc.0

IL_0019:  ldc.i4.1

IL_001a:  add

IL_001b:  stloc.0

IL_001c:  ldloc.0

IL_001d:  ldc.i4.s   10

IL_001f:  blt.s      IL_0008

你可以看到在MSIL清单上的顶端StartvValue是被动态装载的,而在末端,EndValue被强制成为了10.

个人总结:

1. Const是在编译时赋值,而ReadOnly是在运行时赋值;

2. Const只能初始化成String || Numric类型,而ReadOnly可以初始化成任何类型;

3. Const性能略优于ReadOnly,但灵活性不如ReadOnly

4. Const仅仅用于定义相对不变化的值.其余时候均用ReadOnly.

posted on 2008-01-29 10:13  喝水的猪  阅读(2928)  评论(2编辑  收藏  举报