const与readonly深度分析(.NET)
前言
很多.NET的初学者对const和readonly的使用很模糊,本文就const和readonly做一下深度分析,包括:
1. const数据类型的优势
2. const数据类型的劣势
3. readonly类型的优势
4. readonly类型的劣势
5. 编译器对const数据类型如何做处理
6. 编译器对readonly数据类型如何做处理
const常量
编译器在编译const常量的时候会直接把常量值嵌入到IL代码中,这是const常量的优势,即在程序运行过程中不需要通过类型对象或者实例对象检索字段的值,这样多多少少能提高性能。为了更直观的看一下const常量在IL代码下的存在状态,我们举一个例子。
注释:IL代码是编译器编译完成生成的代码,我们所见到的托管dll文件和托管exe文件内部其实都是这种代码。
const常量应用实例
1. 在VS中新建一个解决方案,命名为ConstantTest,在解决方案中添加两个项目,分别为ClassTest(类库项目)和ConstantTest(控制台项目)
2. 在ClassTest项目中添加类ClassTemp,ClassTemp的具体代码如下:
using System;
namespace ClassTest
{
public class ClassTemp
{
public const Int32 num_X = ;
public const Int32 num_Y = ;
public const String rightInfo = "Calculate right!";
public const String errorInfo = "Calculate error!";
}
}
3. 在项目ConstantTest项目中引用ClassTest.dll,在Program.cs中添加如下代码:
using System;
using ClassTest;
namespace ConstantTest
{
class Program
{
static void Main(string[] args)
{
try
{
Int32 x = ClassTemp.num_X;
Int32 y = ClassTemp.num_Y;
Int32 result = x * y;
Console.WriteLine(ClassTemp.rightInfo + " result: " + result);
}
catch (Exception)
{
Console.WriteLine(ClassTemp.errorInfo);
}
finally
{
Console.ReadKey();
}
}
}
}
4. 然后编译项目ConstantTest,用ildasm查看生成的ConstantTest.exe文件,结果如下:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// 代码大小 67 (0x43)
.maxstack
.locals init ([] int32 x,
[] int32 y,
[] int32 result)
IL_0000: nop
.try
{
.try
{
IL_0001: nop
IL_0002: ldc.i4.s 10
IL_0004: stloc.
IL_0005: ldc.i4.s 20
IL_0007: stloc.
IL_0008: ldloc.
IL_0009: ldloc.
IL_000a: mul
IL_000b: stloc.
IL_000c: ldstr
"Calculate right!result: "
IL_0011: ldloc.
IL_0012: box [mscorlib]System.Int32
IL_0017: call string [mscorlib]System.String::Concat(object,
object)
IL_001c: call void [mscorlib]System.Console::WriteLine(string)
IL_0021: nop
IL_0022: nop
IL_0023: leave.s IL_0035
} // end .try
catch [mscorlib]System.Exception
{
IL_0025: pop
IL_0026: nop
IL_0027: ldstr
"Calculate error!"
IL_002c: call void [mscorlib]System.Console::WriteLine(string)
IL_0031: nop
IL_0032: nop
IL_0033: leave.s IL_0035
} // end handler
IL_0035: nop
IL_0036: leave.s IL_0041
} // end .try
finally
{
IL_0038: nop
IL_0039: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
IL_003e: pop
IL_003f: nop
IL_0040: endfinally
} // end handler
IL_0041: nop
IL_0042: ret
} // end of method Program::Main
不用去理解上面IL代码各行的意思,我们只需要确定我们在代码中使用的常量值在IL代码中确实是以
10 20 Calculate right! Calculate error!
出现的,这也说明我们开始做的结论是正确的。
const常量的问题
但是const常量的这种性质也带来了缺陷,就是针对于版本控制出现的问题,就拿上面的列子来说,如果上面的两个项目分别由两个不同的人甚至公司来完成的(公司A和B),现在公司A要对ClassTemp.dll进行升级,比如把里面的num_X改成30,如果我们把升级后的ClassTemp.dll放到ConstantTest的项目中,ConstantTest的执行结果是不会变的(还是400),因为ConstantTest.exe的IL代码之前已经确定了,除非我们重现编译ConstantTest项目,如果对于项目特多的情况这样显然不方便。
因为常量的值是由编译器来识别的,所以常量的类型只能是基元类型,这是const常量的另一个劣势
readonly字段的优势
相比const常量来说,readonly字段在版本控制方面就好了很多,我们还是拿上面的列子来说,不过相应的数据得改一下,直接把修改后的代码贴出来:
ClassTemp.cs
using System;
namespace ClassTest
{
public class ClassTemp
{
public static readonly Int32 num_XR = ;
public static readonly Int32 num_YR = ;
public static readonly String rightInfoR = "Calculate right!";
public static readonly String errorInfoR = "Calculate error!";
}
}
ConstantTest—>Program.cs
using System;
using ClassTest;
namespace ConstantTest
{
class Program
{
static void Main(string[] args)
{
try
{
Int32 x = ClassTemp.num_XR;
Int32 y = ClassTemp.num_YR;
Int32 result = x * y;
Console.WriteLine(ClassTemp.rightInfoR + " result: " + result);
}
catch (Exception)
{
Console.WriteLine(ClassTemp.errorInfoR);
}
finally
{
Console.ReadKey();
}
}
}
}
先看一下结果:
把num_XR改成30,重新编译ClassTemp项目,运行程序结果如下:
结果确实发生了改变,我们下面说一下readonly这种字段是怎么加载的,在说明之前,贴出IL代码
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// 代码大小 78 (0x4e)
.maxstack
.locals init ([] int32 x,
[] int32 y,
[] int32 result)
IL_0000: nop
.try
{
.try
{
IL_0001: nop
IL_0002: ldsfld int32 [ClassTest]ClassTest.ClassTemp::num_XR
IL_0007: stloc.
IL_0008: ldsfld int32 [ClassTest]ClassTest.ClassTemp::num_YR
IL_000d: stloc.
IL_000e: ldloc.
IL_000f: ldloc.
IL_0010: mul
IL_0011: stloc.
IL_0012: ldsfld string [ClassTest]ClassTest.ClassTemp::rightInfoR
IL_0017: ldstr " result: "
IL_001c: ldloc.
IL_001d: box [mscorlib]System.Int32
IL_0022: call string [mscorlib]System.String::Concat(object,
object,
object)
IL_0027: call void [mscorlib]System.Console::WriteLine(string)
IL_002c: nop
IL_002d: nop
IL_002e: leave.s IL_0040
} // end .try
catch [mscorlib]System.Exception
{
IL_0030: pop
IL_0031: nop
IL_0032: ldsfld string [ClassTest]ClassTest.ClassTemp::errorInfoR
IL_0037: call void [mscorlib]System.Console::WriteLine(string)
IL_003c: nop
IL_003d: nop
IL_003e: leave.s IL_0040
} // end handler
IL_0040: nop
IL_0041: leave.s IL_004c
} // end .try
finally
{
IL_0043: nop
IL_0044: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
IL_0049: pop
IL_004a: nop
IL_004b: endfinally
} // end handler
IL_004c: nop
IL_004d: ret
} // end of method Program::Main
CLR加载字段
CLR支持静态字段和非静态字段,我们上面使用的静态字段,静态字段是在类型对象中加载的,类型对象是在类型加载到一个AppDomain时创建的(AppDomain可以暂时理解为程序运行的内存空间),而类型第一次有方法被调用的时候(包含构造方法)会加载到AppDomain中,我们画一个图可能看的更清晰一些:
这样每次启动程序的时候ClassTemp类中的字段值都会重新赋值,所以即使不重新编译ConstantTest项目,依然能得到正确结果。
另外,readonly字段可以是任何类型而不拘泥于基元类型。
注意:readonly字段只能在一个构造方法中写入,另外,利用反射页可以修改字段的值。
如果您有什么意见或者文章对您有任何的帮助,请在评论区告诉我!!!
const与readonly深度分析(.NET)的更多相关文章
- C语言-const和volatile深度分析
1.const只读变量 const修饰的变量是只读的.本质还是变量 const修饰的局部变量在栈上分配空间 const修饰的全局变量在全局数据区分配空间 const只在编译期有用,在运行期无用 con ...
- Java的Final和C#的Const,Readonly比较分析(转载)
Java里面没有readonly关键字,预留了const的关键字,目前还没有实际用途,在Java中,跟这两个关键字比较接近的是final; C#中,两者都存在并可用.两者修饰的全局变量或局部变量都不能 ...
- C#面试-关于const和readonly(看了一个点赞很多的帖子有感而发!)
前景提要: 最近大家都在面试,讨论最多.最基础的问题,莫过于“关于const和readonly常见的笔试题剖析”,等等的大牛解析.我就是一个小菜,只不过,有点不敢苟同大牛的意见.废话少说,进入重点. ...
- 深度分析 Java 的枚举类型:枚举的线程安全性及序列化问题(转)
写在前面: Java SE5 提供了一种新的类型 Java的枚举类型,关键字 enum 可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用,这是一种非常有用的功能 ...
- 深度分析Java的枚举类型—-枚举的线程安全性及序列化问题
原文:深度分析Java的枚举类型--枚举的线程安全性及序列化问题 枚举是如何保证线程安全的 要想看源码,首先得有一个类吧,那么枚举类型到底是什么类呢?是enum吗?答案很明显不是,enum就和clas ...
- 宋牧春: Linux设备树文件结构与解析深度分析(2) 【转】
转自:https://mp.weixin.qq.com/s/WPZSElF3OQPMGqdoldm07A 作者简介 宋牧春,linux内核爱好者,喜欢阅读各种开源代码(uboot.linux.ucos ...
- PhpStudy BackDoor2019 深度分析
笔者<Qftm>原文发布<合天>:https://mp.weixin.qq.com/s?__biz=MjM5MTYxNjQxOA==&mid=2652852661&am ...
- C#基础知识七之const和readonly关键字
前言 不知道大家对const和readonly关键字两者的区别了解多少,如果你也不是很清楚的话,那就一起来探讨吧!探讨之前我们先来了解静态常量和动态常量. 静态常量 所谓静态常量就是在编译期间会对变量 ...
- const 与 readonly知多少
原文地址: http://www.cnblogs.com/royenhome/archive/2010/05/22/1741592.html 尽管你写了很多年的C#的代码,但是可能当别人问到你cons ...
随机推荐
- Pinterest 架构:两年内月 PV 从零到百亿【翻译】
原文地址 这篇文章,采用 Markdown 方式,写的还是比较实在的,要是有架构图就好了. Pinterest 是图片版的 Twitter,用户把自己感兴趣的东西用图钉(Pins)钉在钉板(PinBo ...
- Ext4,Ext3的特点和区别(转)
Linux kernel 自 2.6.28 开始正式支持新的文件系统 Ext4. Ext4 是 Ext3 的改进版,修改了 Ext3 中部分重要的数据结构,而不仅仅像 Ext3 对 Ext2 那样,只 ...
- Javascript - 栈 和 单链表
最近在重温数据结构,于是写了一些代码玩玩,都是很初级的,表喷各位.... function Stack() { this.dataStore = []; this.top = 0; } Stack.p ...
- 【转】安装OpenSSL缺失Microsoft Visual C++ 2008 Redistributables的解决方案
from: http://www.cnblogs.com/luguo3000/p/3539815.html 在安装OpenSSL的时候通常会提示以下错误: "The Win32 OpenSS ...
- NYOJ 741 "数学家"ST
"数学家"ST 时间限制:1000 ms | 内存限制:65535 KB 难度:2 描写叙述 ST是个非常特别的人类,不仅喜欢做一些不同平常的事,并且对于在无聊的时候怎样打发时 ...
- [知识库分享系列] 二、.NET(ASP.NET)
最近时间又有了新的想法,当我用新的眼光在整理一些很老的知识库时,发现很多东西都已经过时,或者是很基础很零碎的知识点.如果分享出去大家不看倒好,更担心的是会误人子弟,但为了保证此系列的完整,还是选择分享 ...
- Android应用中动态更改主题的实现
在android应用程序中我们可能需要切换模式,如晚上切换到夜间模式便于阅读等.本文参考了网上的一些资料,并结合实例,实现了动态更改主题的效果. Android中实现theme主题可以使用在activ ...
- 修改windows系統下xampp中apache端口被其他程式占用的問題
windows 7安裝後啟動xampp, 提示port 443 被其他程式占用. 網上查找解決方案: http://stackoverflow.com/questions/21182512/how-t ...
- CentOS下Red5安装
Red5介绍 Red5是一个采用Java开发开源的Flash流媒体服务器.它支持:把音频(MP3)和视频(FLV)转换成播放流: 录制客户端播放流(只支持FLV):共享对象:现场直播流发布:远程调用. ...
- 【Spark】jupyter notebook
iPython 和 Jupter Notebook 都支持spark ,调用方式如下: PYSPARK_DRIVER_PYTHON=ipython ./bin/pysparkPYSPARK_DRIVE ...