C#基本知识点-Readonly和Const的区别

目录

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

静态常量(Const)和动态常量(Readonly)之间的区别

动态常量(Readonly)被赋值后不可以改变

总结

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

先解释下什么是静态常量(Const)以及什么是动态常量(Readonly)。

静态常量(Const)是指编译器在编译时候会对常量进行解析,并将常量的值替换成初始化的那个值。

动态常量(Readonly)的值则是在运行的那一刻才获得的,编译器编译期间将其标示为只读常量,而不用常量的值代替,这样动态常量不必在声明的时候就初始化,而可以延迟到构造函数中初始化。

静态常量(Const)和动态常量(Readonly)之间的区别

静态常量(Compile-time Constant)动态常量(Runtime Constant)

1.Const修饰的常量在声明的时候必须初始化;Readonly修饰的常量则可以延迟到构造函数初始化 。

2.Const常量既可以声明在类中也可以在函数体内,但是Static Readonly常量只能声明在类中。Const是静态常量,所以它本身就是Static的,因此不能手动再为Const增加一个Static修饰符。

3.Const修饰的常量在编译期间就被解析,即:经过编译器编译后,我们都在代码中引用Const变量的地方会用Const变量所对应的实际值来代替; Readonly修饰的常量则延迟到运行的时候。

举个例子来说明一下:


以上是语法方面的应用,那在实际的用法上,还是有些微妙的变化,通常不易发觉.

举个例子来说明一下:

在程序集DoTestConst.dll 中有一个类MyClass,定义了一个公开的静态变量Count

publicstaticclassMyClass

{publicconstintCount =10;

}

然后另外一个应用程序中引用DoTestConst.dll,并在代码中作如下调用:

publicstaticvoidMain(string[] args)

{

Console.WriteLine(DoTestConst.MyClass.Count);//输出10Console.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的版本:

publicstaticclassMyClass

{publicstaticreadonlyintCount =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, 只读)

对于引用类型变量,引用本身(相当于指针)不可改变。

值类型变量,举个例子说明一下:

publicclassStudent

{publicreadonlyintAge;publicStudent(intage)

{this.Age =age;

}

}

Student的实例Age在构造函数中被赋值以后就不可以改变,下面的代码不会编译通过:

Student student =newStudent(20);

student.Age=21;//错误信息:无法对只读的字段赋值(构造函数或变量初始化器中除外)

引用类型变量,举个例子说明一下:

publicclassStudent

{publicintAge;//注意这里的Age是没有readonly修饰符的publicStudent(intage)

{this.Age =age;

}

}publicclassSchool

{publicreadonlyStudent Student;publicSchool(Student student)

{this.Student =student;

}

}

School实例的Student是一个引用类型的变量,赋值后,变量不能再指向其他任何的Student实例,所以,下面的代码将不会编译通过:

School school =newSchool(newStudent(10));

school.Student=newStudent(20);//错误信息:无法对只读的字段赋值(构造函数或变量初始化器中除外)

引用本身不可以改变,但是引用说指向的实例的值是可以改变的。所以下面的代码是可以编译通过的:

School school =newSchool(newStudent(10));

school.Student.Age=20;

在构造方法中,我们可以多次对Readonly修饰的常量赋值。举个例子说明一下:

publicclassStudent

{publicreadonlyintAge =20;//注意:初始化器实际上是构造方法的一部分,它其实是一个语法糖publicStudent(intage)

{this.Age =age;this.Age =25;this.Age =30;

}

}

总结

Const和Readonly的最大区别(除语法外)

Const的变量是嵌入在IL代码中,编译时就加载好,不依赖外部dll(这也是为什么不能在构造方法中赋值)。Const在程序集更新时容易产生版本不一致的情况。

Readonly的变量是在运行时加载,需请求加载dll,每次都获取最新的值。Readonly赋值引用类型以后,引用本身不可以改变,但是引用所指向的实例的值是可以改变的。在构造方法中,我们可以多次对Readonly赋值。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容