什么是静态常量(Const)和动态常量(Readonly)

 
  先解释下什么是静态常量(Const)以及什么是动态常量(Readonly)。
  静态常量(Const)是指编译器在编译时候会对常量进行解析,并将常量的值替换成初始化的那个值。
  动态常量(Readonly)的值则是在运行的那一刻才获得的,编译器编译期间将其标示为只读常量,而不用常量的值代替,这样动态常量不必在声明的时候就初始化,而可以延迟到构造函数中初始化。
 
静态常量(Const)和动态常量(Readonly)之间的区别
 
 
 
静态常量(Compile-time Constant)
 
动态常量(Runtime Constant)
 
定义
 
声明的同时要设置常量值。
 
声明的时候可以不需要进行设置常量值,可以在类的构造函数中进行设置。
 
类型限制
 
只能修饰基元类型,枚举类型或者字符串类型。
 
没有限制,可以用它定义任何类型的常量。
 
对于类对象而言
 
对于所有类的对象而言,常量的值是一样的。
 
对于类的不同对象而言,常量的值可以是不一样的。
 
内存消耗
 
无。
 
要分配内存,保存常量实体。
 
综述
 
性能要略高,无内存开销,但是限制颇多,不灵活。
 
灵活,方便,但是性能略低,且有内存开销。
 
Const修饰的常量在声明的时候必须初始化;Readonly修饰的常量则可以延迟到构造函数初始化 。
Const常量既可以声明在类中也可以在函数体内,但是Static Readonly常量只能声明在类中。Const是静态常量,所以它本身就是Static的,因此不能手动再为Const增加一个Static修饰符。
Const修饰的常量在编译期间就被解析,即:经过编译器编译后,我们都在代码中引用Const变量的地方会用Const变量所对应的实际值来代替; Readonly修饰的常量则延迟到运行的时候。
  举个例子来说明一下:
 
 
 
        public static readonly int NumberA = NumberB * 10;
        public static readonly int NumberB = 10;
 
        public const int NumberC = NumberD*10;
        public const int NumberD = 10;
 
        static void Main(string[] args)
        {
            Console.WriteLine("NumberA is {0}, NumberB is {1}.", NumberA, NumberB);//NumberA is 0, NumberB is 10.
            Console.WriteLine("NumberC is {0}, NumberD is {1}.", NumberC, NumberD);//NumberC is 100, NumberD is 10.
            Console.ReadKey();
        }
 
  以上是语法方面的应用,那在实际的用法上,还是有些微妙的变化,通常不易发觉.
  举个例子来说明一下:
  在程序集DoTestConst.dll 中有一个类MyClass,定义了一个公开的静态变量Count
 
    public static class MyClass
    {
        public const int Count = 10;
    }
  然后另外一个应用程序中引用DoTestConst.dll,并在代码中作如下调用:
 
    public static void Main(string[] args)
    {
        Console.WriteLine(DoTestConst.MyClass.Count);//输出10
        Console.ReadKey();
    }
  毫无疑问,非常简单的代码,直接输出10。
  接下来更新MyClass的Count的值为20,然后重新编译DoTestConst.dll,并更新到应用程序的所在目录中,注意不要编译应用程序。那么这时候的输出结果按预期那么想应该是20才对,但实际上还是10,为什么呢?
  这就是Const的特别之处,有多特别还是直接看生成的IL,查看IL代码(假设这时候Count的值为10)
 
  IL_0000: nop
  IL_0001: ldc.i4.s 10
  IL_0003: call void [mscorlib]System.Console::WriteLine(int32)
 
  红色代码很明显的表明了,直接加载10,没有通过任何类型的加载然后得到对应变量的,也就是说在运行时没有去加载DoTestConst.dll,那么是否意味着没有DoTestConst.dll也可以运行呢?答案是肯定的,删除DoTestConst.dll也可以运行,是否很诡异呢?也就解释了之前的实验,为什么更新Const变量的值之后没有调用新的值,因为程序在运行的时候根本不会去加载DoTestConst.dll。那么10这个值是从哪来的呢?实际上CLR对于Const变量做了特殊处理,是将Const的值直接嵌入在生成的IL代码中,在执行的时候不会再去从dll加载。这也带来了一个不容易发觉的Bug,因此在引用其他程序集的Const变量时,需考虑到版本更新问题,要解决这个问题就是把调用的应用程序再编译一次就ok了。但实际程序部署更新时可能只更新个别文件,这时候就必须用Readonly关键字来解决这个问题。
 
  接下来看Readonly的版本:
 
    public static class MyClass
    {
        public static readonly int Count = 10;
    }
  调用方代码不变,接着看生成的IL代码:
 
  IL_0000: nop
  IL_0001: ldsfld int32 [DoTestConst]DoTestConst.MyClass::Count
  IL_0006: call void [mscorlib]System.Console::WriteLine(int32)
 
  很明显加载代码变了,一个很常见的ldsfld动作,请求了DoTestConst.MyClass的Count变量,是通过强制要求加载DoTestConst来实现的。因此这时候更新Count的值重新编译之后,还是不编译调用程序,然后再执行就会看到新的值。而这时候如果删除DoTestConst.dll那么,会出现找不到dll之类的异常。这也充分说明了对于Readonly定义的变量是在运行时加载的。
 
动态常量(Readonly)被赋值后不可以改变
 
  ReadOnly 变量是运行时变量,它在运行时第一次赋值后将不可以改变。其中“不可以改变”分为两层意思:
 
对于值类型变量,值本身不可以改变(Readonly, 只读)
对于引用类型变量,引用本身(相当于指针)不可改变。
  值类型变量,举个例子说明一下:
 
    public class Student
    {
        public readonly int Age;
 
        public Student(int age)
        {
            this.Age = age;
        }
    }
 
  Student的实例Age在构造函数中被赋值以后就不可以改变,下面的代码不会编译通过:
 
Student student = new Student(20);
student.Age = 21; //错误信息:无法对只读的字段赋值(构造函数或变量初始化器中除外)
  引用类型变量,举个例子说明一下:
 
 
    public class Student
    {
        public int Age; //注意这里的Age是没有readonly修饰符的
 
        public Student(int age)
        {
            this.Age = age;
        }
    }
 
    public class School
    {
        public readonly Student Student;
 
        public School(Student student)
        {
            this.Student = student;
        }
    }
 
  School实例的Student是一个引用类型的变量,赋值后,变量不能再指向其他任何的Student实例,所以,下面的代码将不会编译通过:
 
School school = new School(new Student(10));
school.Student = new Student(20);//错误信息:无法对只读的字段赋值(构造函数或变量初始化器中除外)
  引用本身不可以改变,但是引用说指向的实例的值是可以改变的。所以下面的代码是可以编译通过的:
 
School school = new School(new Student(10));
school.Student.Age = 20;
  在构造方法中,我们可以多次对Readonly修饰的常量赋值。举个例子说明一下:
 
 
    public class Student
    {
        public readonly int Age = 20;//注意:初始化器实际上是构造方法的一部分,它其实是一个语法糖
 
        public Student(int age)
        {
            this.Age = age;
            this.Age = 25;
            this.Age = 30;
        }
    }
 
总结
 
  Const和Readonly的最大区别(除语法外)
  Const的变量是嵌入在IL代码中,编译时就加载好,不依赖外部dll(这也是为什么不能在构造方法中赋值)。Const在程序集更新时容易产生版本不一致的情况。
Readonly的变量是在运行时加载,需请求加载dll,每次都获取最新的值。Readonly赋值引用类型以后,引用本身不可以改变,但是引用所指向的实例的值是可以改变的。在构造方法中,我们可以多次对Readonly赋值。

C#基本知识点-Readonly和Const的区别的更多相关文章

  1. readonly和const的区别

    readonly与const的区别1.const常量在声明的同时必须赋值,readonly在声明时可以不赋值2.readonly只能在声明时或在构造方法中赋值(readonly的成员变量可以根据调用不 ...

  2. c#:readonly与const的区别

    readonly与const的区别: 1.初始化:const  字段只能在该字段的声明中初始化. readonly  字段可以在声明或构造函数中初始化. 2.值: const 字段是编译时常量(con ...

  3. C# readonly和const的区别

    什么是静态常量(Const)和动态常量(Readonly) 先解释下什么是静态常量(Const)以及什么是动态常量(Readonly). 静态常量(Const)是指编译器在编译时候会对常量进行解析,并 ...

  4. readonly与const的区别

    readonly 关键字与 const 关键字不同.const 字段只能在该字段的声明中初始化.readonly字段可以在声明或构造函数中初始化.因此,根据所使用的构造函数,readonly字段可能具 ...

  5. C# 总结const、 readonly、 static三者区别:

    总结const. readonly. static三者区别: (有人问我,看似简单,我也没能立刻回答出来,总结一下,分享一下.) const:静态常量,也称编译时常量(compile-time con ...

  6. 编写高质量代码改善C#程序的157个建议——建议6: 区别readonly和const的使用方法

    建议6: 区别readonly和const的使用方法 很多初学者分不清readonly和const的使用场合.在我看来,要使用const的理由只有一个,那就是效率.但是,在大部分应用情况下, “效率” ...

  7. 我所理解的readonly和const

    最近要给学校软件小组新成员讲几次课,所以把很多以前懒得学习的和模糊不清的知识点,重新学习了一下. MSDN是这样解释的: readonly 关键字与 const 关键字不同. const 字段只能在该 ...

  8. readonly与const

    readonly与const 在C#中,readonly 与 const 都是定义常量,但不同之处在于:readonly 是运行时常量,而 const 是编译时常量. ; public void Te ...

  9. Readonly与const初识

    对于readonly和const,很多人无法具体区分,不清楚它们的具体使用场合:现在我们分析它们之间的区别和使用场合. const是一个编译期常量:const只能用于修饰基元类型.枚举类型或者字符串类 ...

随机推荐

  1. webview加载本地html

    //webView.loadUrl("file:///android_asset/index.html"); 加载assets目录中含有的index.html  webView.l ...

  2. win下Maven安装和基本设置

    注:本文介绍 Windows 平台上 Maven 的安装.Maven 3 需要运行在 JDK1.4 以上的版本上. 非原创:原创地址 http://www.ibm.com/developerworks ...

  3. 剑指offer--面试题15--相关

    感受:清晰的思路是最重要的!!! 题目:求链表中间节点 ListNode* MidNodeInList(ListNode* pHead) { if(pHead == NULL) return NULL ...

  4. 阿里云ubuntu12.04下安装使用mongodb

    阿里云ubuntu12.04下安装mongodb   apt-get install mongodb 阿里云ubuntu12.04下卸载mongodb,同时删除配置文件     apt-get pur ...

  5. oracle Execute Immediate 用法

      包含using into用法. Declare        v_sid Integer:=20020101;        v_sql Varchar2(100);        v_resul ...

  6. Windows Server 2008下共享资源访问走捷径 (不用用户名 和 密码 访问共享)

    1. 启用来宾帐号2. 共享目录添加“Guest”帐号3. “gpedit.msc”,打开对应系统的组策略编辑窗口;在该编辑窗口的左侧显示区域,依次展开“本地计算机策略”/“计算机配置”/“Windo ...

  7. JS内存管理测试

    打开调试器,切换到timer,点击左下角的record按钮开始,切换到memory视图,在文档中点击鼠标左右键,看股价走势图 function Allocate(kbs){ this.mem = ne ...

  8. 关于Backtracing中有重复元素的处理办法

    backtracing是一个常用的解法.之前遇到一个题目,求一个集合的子集, 例如给定{1,2,3,4,5},求其大小为3的子集. 利用backtracing可以较快的给出答案. 然而,该题还有一个变 ...

  9. Installing Lua in Mac

    Lua is distributed in source form. You need to build it before using it. Building Lua should be stra ...

  10. Relevance Between Variable Declaration and Definition in C++

    A declaration makes a name known to a programm. A definition creates the assocatied entity. A variab ...