Fork me on GitHub

Effective C# 条款2

 图1编译时常量和运行时常量区别

 

  编译时常量比运行时常量稍微快一点,但却缺乏灵活性,但如果程序“慢”能确保正确运行,“快”有可能导致出错,那么我相信大家考虑到“常量之间何必在乎那一点点误差呢”当然稳定性首选的是运行时常量。

// 编译时常量声明:

public const int _Millennium = 2000;



// 运行时常量声明:

public static readonly int _ThisYear = 2011;

 

                         图2 运行时和编译时常量的MSIL代码

  通过比较编译时常量和运行时常量的MSIL代码发现,编译常量在编译后为变量提供常量值,而运行时常量并没有提供常量值。

  我们知道,运行时常量和编译时常量最重要的区别就在于运行时常量值的辨析发生在运行时,而编译时常量值的辨析发生编译时。换言之,使用运行时常量编译后的 IL 代码引用的是 readonly 变量,而非它的值;而使用编译时常量编译后的 IL 代码将直接引用它的值——就像我们直接在代码中使用常量值一样。即使我们使用的是数值常量并跨程序集引用,情况也是一样:如果在程序集 A 中引用程序集 B 中的常量,那么编译后程序集 A 中出现的那个常量将被它的值所替换。这种差别对于代码的维护性而言有着相当的影响。

现在让我们看一下它们的区别:

/// <summary>
/// 定义运行时常量和编译时常量
/// </summary>
public class TestClass
{
public const int isConstVar = 5;
public static readonly int inReadonlyVar = 1;
}
/// <summary>
/// 测试运行时和编译时常量区别
/// </summary>
class Program
{
static void Main(string[] args)
{
for (int i = TestClass.TestClass.inReadonlyVar; i < TestClass.TestClass.isConstVar; i++)
{
Console.WriteLine(
"This is {0}", i);
}
Console.ReadKey();
}
}

      图3输出结果

图4编译常量和运行常量MSIL

 

 

  通过上面我们可以发现编译时常量是通过hard code方式替换为一个值常量,而运行时常量是通过一个引用方式来,如果我们修改编译时常量值为10,而运行时常量值为3,修改如下:

public class TestClass
{
public const int isConstVar = 10;
public static readonly int inReadonlyVar = 3;
}

  

  如果我们重新编译TestClass,然而不重新编译Program控制台应用程序,而且把重新编译后的TestClass copy到控制台应用程序的bin中再次运行,大家可以猜猜看最后输出的结果,我相信聪明的你肯定可以猜得到结果,没错结果如下:

    图5输出结果

 

 

  分析原因在于C#编译器在第一次编译控制台应用程序集时,将其中的 isConstVar替换成了它对应的常量值5,而对于 inReadonlyVar来说,由于它被声明为 readonly,所以它的辨析发生在运行时。因此,控制台应用程序集在没有被重新编译的情况下, 仍然可以使用新的 inReadonlyVar值。

posted @ 2011-04-05 15:01  JK_Rush  阅读(530)  评论(0编辑  收藏  举报