当前位置: 首页 > news >正文

条款二 : 运行时常量(readonly)优于编译时常量(const)

    C#语言有两种不同的常量机制:一种为编译时(complie-time)常量,一种为运行时(runtime)常量。两种常量有着迥异的行为,使用不正确会导致程序的性能下降或者出现错误。编译时常量比运行时常量稍微快一点,但却缺乏灵活性。只有在性能非常关键,并且其值永远不会变的情况下,我们才应该使用编译时常量。
    在C#中,我们使用readonly关键字来声明运行时常量,用const关键字来声明编译时常量。
    
    // 编译时常量:
    public   const   int  _Millennium  =   2000 ;
   
// 运行时常量:
     public   static   readonly   int  _ThisYear  =   2004 ;

    编译时常量与运行时常量行为的不同处在于它们的访问方式。编译时常量在编译后的结果代码中会被替换为该常量的值。
    
if (myDateTime.Year  ==  _Millennium)

    其编译后的IL和下面的代码编译后的IL一样:
    
if (myDateTime.Year  ==   2000 )

    运行时常量的值则在运行时被计算。对于使用运行时常量的代码,其编译后的IL将维持对readonly变量(而非它的值)的引用。
    这种差别会为我们使用两种常量类型带来一些限制。编译时常量只可以用于基元类型(包括内建的整数类型和浮点类型)、枚举类型或字符串类型。因为只有这些类型才允许我们在初始化器中指定有意义的常量值。(声明的同时初始化)。在使用这些常量的代码编译后得到的IL代码中,常量直接被替换为它们的字面值(literal)。例如,下面的代码就不会通过编译。事实上,C#不允许我们使用new操作符来初始化一个编译时常量,即使被初始化的常量类型是一个值类型。
    
// 下面的代码不会通过编译,但是换成readonly就可以:
private   const  DateTime _classCreation  =   new  DateTime( 2000 1 1 0 0 0 );

    编译时常量仅限于数值和字符串。只读(readonly)字段之所以也被称作一种常量,是因为它们的构造器一旦被执行,我们将不能对它们的值做任何修改。与编译时常量不同的地方在于,只读字段的赋值操作发生在运行时,因此它们具有更多的灵活性。比如,只读字段的类型就没有任何限制。对于只读字段,我们只能在构造器或者初始化器中为它们赋值。在上面的代码中,我们可以声明readonly的DateTime结构变量,但是不能声明const的DateTime结构变量。
    我们可以声明readonly的实例常量,从而为一个类型的每个实例存储不同的值。但是const修饰的编译时常量默认就被定义为静态常量。
    我们知道,运行时常量和编译时常量最重要的区别就在于运行时常量值的辨析发生在运行时,而编译时常量值的辨析发生在编译时。换言之,使用运行时常量编译后的IL代码引用的是readonly变量,而非它的值;而使用编译时常量编译后的IL代码将直接引用它的值---就像我们直接在代码中使用常量值一样。即使我们使用的是数值常量并跨程序集引用,情况也是一样:如果在程序集A中引用程序集B中的常量,那么编译后程序集A中出现的那个常量将被它的值所替换。这种差别对于代码的维护性而言有着相当的影响。
    编译时常量与运行时常量被解析的方式影响着运行时的兼容性。假设我们在一个名为Infrastructure的程序集中分别定义了一个const字段和一个readonly字段:
    
public   class  UsefulValues
{
   
public static readonly int StartValue = 5;
   
public const int EndValue = 10;
}


    在另外一个程序集Application中,我们又引用着这些值:
    
      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程序集分发出去,但并没有重新编译Application程序集。我们期望得到如下的输出:
     Value is 105
     Value is 106
     ……
     Value is 109
     但实际上,我们却没有得到任何输出。因为现在那个循环语句将使用105作为它的起始值,使用10作为它的结束条件。其根本原因在于C#编译器在第一次编译Application程序集时,将其中的EndValue替换成了它对应的常量值10。而对于StartValue来说,由于它被声明为readonly,所以它的解析发生在运行时。因此,Application程序集在没有被重新编译的情况下,仍然可以使用新的StartVale值。为了改变所有使用readonly常量的客户代码的行为,简单地安装一个新版的Infrastructure程序集就足够了。“更改一个运行时常量的值”应该被视作对类型接口的更改,其后果是我们必须重新编译所有引用该常量的代码。
     使用const较之于使用readonly的唯一好处就是性能:使用已知常量值的代码效率要比访问readonly值的代码效率稍好一点。但是这其中的效率提升势非常小的,大家应该和其所失去的灵活性进行一番权衡比较。在打算放弃灵活性之前,一定要对两者的性能差别做一个评测。
     综上所述,只有当某些情况要求变量的值必须在编译时可用,才应该考虑使用const,例如:特性(attribute)类的参数,枚举定义,以及某些不随组件版本变化而改变的值。否则,对于其他任何情况,都应该优先选择readonly常量,从而获得其所具有的灵活性。

相关文章:

  • SQL Server 2005新功能
  • 是DataGrid的某数字内容列可编辑,并且以NumericStepper组件改变数值。
  • 找工作要做的十件事
  • 什么是数据恢复技术
  • [InnoDB系列] -- SHOW INNODB STATUS 探秘
  • 服务器中的地震仪,MOM2005+SP1部署指南(MOM2005系列之一)
  • 网络硬盘drop.io的使用
  • 1904年 圣路易斯 第三届奥运会
  • 20080605-昨日回顾今日计划
  • 机器人也有女性,Sega E.M.A登场
  • Mastering Oracle SQL学习笔记(join句法专题第八部份)
  • Microsoft .Net 框架 SDK 快速入门教程
  • 杂语
  • SAP BC427 课程中文自学笔记
  • c#中实现类似js的Eval方法
  • 分享的文章《人生如棋》
  • [分享]iOS开发-关于在xcode中引用文件夹右边出现问号的解决办法
  • 230. Kth Smallest Element in a BST
  • dva中组件的懒加载
  • Effective Java 笔记(一)
  • flutter的key在widget list的作用以及必要性
  • github指令
  • Java编程基础24——递归练习
  • node入门
  • Python3爬取英雄联盟英雄皮肤大图
  • SpingCloudBus整合RabbitMQ
  • SpringCloud集成分布式事务LCN (一)
  • 机器人定位导航技术 激光SLAM与视觉SLAM谁更胜一筹?
  • 配置 PM2 实现代码自动发布
  • 入职第二天:使用koa搭建node server是种怎样的体验
  • 什么软件可以提取视频中的音频制作成手机铃声
  • 一道闭包题引发的思考
  • 移动互联网+智能运营体系搭建=你家有金矿啊!
  • 这几个编码小技巧将令你 PHP 代码更加简洁
  • 支付宝花15年解决的这个问题,顶得上做出十个支付宝 ...
  • ​secrets --- 生成管理密码的安全随机数​
  • ​水经微图Web1.5.0版即将上线
  • # Maven错误Error executing Maven
  • #git 撤消对文件的更改
  • $ is not function   和JQUERY 命名 冲突的解说 Jquer问题 (
  • (2022 CVPR) Unbiased Teacher v2
  • (初研) Sentence-embedding fine-tune notebook
  • (附源码)springboot 基于HTML5的个人网页的网站设计与实现 毕业设计 031623
  • (五)关系数据库标准语言SQL
  • ***通过什么方式***网吧
  • .net mvc部分视图
  • .NET 命令行参数包含应用程序路径吗?
  • .net 验证控件和javaScript的冲突问题
  • .NET 中使用 TaskCompletionSource 作为线程同步互斥或异步操作的事件
  • .net访问oracle数据库性能问题
  • .net流程开发平台的一些难点(1)
  • .sh 的运行
  • @entity 不限字节长度的类型_一文读懂Redis常见对象类型的底层数据结构
  • @RequestMapping处理请求异常
  • [Apio2012]dispatching 左偏树