什么是静态常量(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. UML类图关系-转

    1.关联 双向关联: C1-C2:指双方都知道对方的存在,都可以调用对方的公共属性和方法. 在GOF的设计模式书上是这样描述的:虽然在分析阶段这种关系是适用的,但我们觉得它对于描述设计模式内的类关系来 ...

  2. 【块状树】BZOJ 1086: [SCOI2005]王室联邦

    1086: [SCOI2005]王室联邦 Time Limit: 10 Sec  Memory Limit: 162 MBSec  Special JudgeSubmit: 826  Solved:  ...

  3. hlsl 和cg 涉及 mul 左乘 右乘

    error: 1. mul' implicit truncation of vector type 2. matrixXXX: array dimensions of(unknown scope en ...

  4. 多线程Java Socket编程示例(转)

    这篇做为学习孙卫琴<<Java网络编程精解>>的学习笔记吧.其中采用Java 5的ExecutorService来进行线程池的方式实现多线程,模拟客户端多用户向同一服务器端发送 ...

  5. Unity3d Detect NetState

    public static bool HasConnection() { System.Net.WebClient client; System.IO.Stream stream; try { usi ...

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

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

  7. django在nginx uwsgi和tornado异步方案在项目中的体验

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://rfyiamcool.blog.51cto.com/1030776/1397495 ...

  8. HDU 1753 大明A+B(字符串模拟,简单题)

    简单题,但要考虑一些细节: 前导0不要,后导0不要,小数长度不一样时,有进位时,逆置处理输出 然后处理起来就比较麻烦了. 题目链接 我的代码纯模拟,把小数点前后分开来处理,写的很繁杂,纯当纪念——可怜 ...

  9. HDU 3461 Code Lock(并查集,合并区间,思路太难想了啊)

    完全没思路,题目也没看懂,直接参考大牛们的解法. http://www.myexception.cn/program/723825.html 题意是说有N个字母组成的密码锁,如[wersdfj],每一 ...

  10. 1009-2的N次方

    描述 编程精确计算2的N次方.(N是介于100和1000之间的整数). 输入 正整数N (100≤N≤1000) 输出 2的N次方 样例输入 200 样例输出 16069380442589902755 ...