前言

很多.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)的更多相关文章

  1. C语言-const和volatile深度分析

    1.const只读变量 const修饰的变量是只读的.本质还是变量 const修饰的局部变量在栈上分配空间 const修饰的全局变量在全局数据区分配空间 const只在编译期有用,在运行期无用 con ...

  2. Java的Final和C#的Const,Readonly比较分析(转载)

    Java里面没有readonly关键字,预留了const的关键字,目前还没有实际用途,在Java中,跟这两个关键字比较接近的是final; C#中,两者都存在并可用.两者修饰的全局变量或局部变量都不能 ...

  3. C#面试-关于const和readonly(看了一个点赞很多的帖子有感而发!)

    前景提要: 最近大家都在面试,讨论最多.最基础的问题,莫过于“关于const和readonly常见的笔试题剖析”,等等的大牛解析.我就是一个小菜,只不过,有点不敢苟同大牛的意见.废话少说,进入重点. ...

  4. 深度分析 Java 的枚举类型:枚举的线程安全性及序列化问题(转)

    写在前面: Java SE5 提供了一种新的类型 Java的枚举类型,关键字 enum 可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用,这是一种非常有用的功能 ...

  5. 深度分析Java的枚举类型—-枚举的线程安全性及序列化问题

    原文:深度分析Java的枚举类型--枚举的线程安全性及序列化问题 枚举是如何保证线程安全的 要想看源码,首先得有一个类吧,那么枚举类型到底是什么类呢?是enum吗?答案很明显不是,enum就和clas ...

  6. 宋牧春: Linux设备树文件结构与解析深度分析(2) 【转】

    转自:https://mp.weixin.qq.com/s/WPZSElF3OQPMGqdoldm07A 作者简介 宋牧春,linux内核爱好者,喜欢阅读各种开源代码(uboot.linux.ucos ...

  7. PhpStudy BackDoor2019 深度分析

    笔者<Qftm>原文发布<合天>:https://mp.weixin.qq.com/s?__biz=MjM5MTYxNjQxOA==&mid=2652852661&am ...

  8. C#基础知识七之const和readonly关键字

    前言 不知道大家对const和readonly关键字两者的区别了解多少,如果你也不是很清楚的话,那就一起来探讨吧!探讨之前我们先来了解静态常量和动态常量. 静态常量 所谓静态常量就是在编译期间会对变量 ...

  9. const 与 readonly知多少

    原文地址: http://www.cnblogs.com/royenhome/archive/2010/05/22/1741592.html 尽管你写了很多年的C#的代码,但是可能当别人问到你cons ...

随机推荐

  1. maven nexus-staging-maven-plugin exception-connect timed out

    不知道是国内的网络的问题还是别的原因,在deploy一个maven的artifact到oss server的时候总是报错: Failed to execute goal org.sonatype.pl ...

  2. 动态生成Table内文字换行。

    后台动态生成table,并把td内的文字进行换行. 前台: <body style="width:100%;height:540px;margin-left:0px;margin-to ...

  3. java单例之enum实现方式

    传统的两私有一公开(私有构造方法.私有静态实例(懒实例化/直接实例化).公开的静态获取方法)涉及线程安全问题(即使有多重检查锁也可以通过反射破坏单例), 目前最为安全的实现单例的方法是通过内部静态en ...

  4. dapper 操作类封装

    using System; using System.Collections.Generic; using System.Data; using System.Data.SQLite; using S ...

  5. MAC 磁盘清理工具 ncdu

    下载命令:brew install ncdu 使用命令:ncdu . 它会将当前目录下的所有文件.文件夹大小安倒叙排列,方便清除

  6. [译] 一、为何要推出AppCoda系列?

    声明:本文翻译自AppCoda网站的文章:Why Launching AppCoda?,作者是创建者Simon Ng.如有异议,请联系博主. 去年九月份,我在App Store上发布了自己第一个iPh ...

  7. git删除远程文件夹或文件的方法

    由于本地修改了文件夹大全名大小写的原因,同步到git上并不区分大小写,造成了一些文件同步不了,所以要先把git远程库上文件夹删除掉,然后再重新同步 如下,我把src里的全部移除,但是本地文件还保留. ...

  8. How to Debug Enterprise Portal Code in Dynamics AX 2009

    转载 To set up debugging for pages1. Log into the server that is running the AOS.2. Open the Microsoft ...

  9. 打开jnlp Faild to validate certificate, the application will not be executed.

    今天连jenkins, 本来好好的,只是我在一台机器上一直不断的启动不同的jnlp,绑定不同命名的slave, 然后突然就报错了, 如下截图所示:

  10. win10如何将此电脑显示在桌面

    一.问题的提出 计算机系统彻底只能用win10的时候,习惯我的电脑显示在桌面上,可是系统并不默认这样 二.问题的解决 1.win10系统的桌面是没有“我的电脑”的图标,在桌面上点击右键,选择“个性化” ...