前言

很多.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. Apache Storm 与 Spark:对实时处理数据,如何选择【翻译】

    原文地址 实时商务智能这一构想早已算不得什么新生事物(早在2006年维基百科中就出现了关于这一概念的页面).然而尽管人们多年来一直在对此类方案进行探讨,我却发现很多企业实际上尚未就此规划出明确发展思路 ...

  2. ubuntu 16.04下安装oracle jdk 1.7

    网上搜索了下,知道了大概,不能用apt装了,oracle也不再提供deb包了.只能下tar.gz包自己装. 先下载下来jdk:http://www.oracle.com/technetwork/jav ...

  3. 解决adb server端口被占用的问题

    先执行adb nodaemon server ,查看adb server的端口是多少 C:\Users\xxxx>adb nodaemon server   cannot bind 'tcp:5 ...

  4. Scala 深入浅出实战经典 第65讲:Scala中隐式转换内幕揭秘、最佳实践及其在Spark中的应用源码解析

    王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...

  5. .net core 1.0 实现负载多服务器单点登录

    前言 .net core 出来有一时间了,这段时间也一直在做技术准备,目前想做一个单点登录(SSO)系统,在这之前用.net时我用习惯了machineKey ,也顺手在.net core 中尝试了一上 ...

  6. SymmetricDS 数据库双向同步开源软件入门

    一句话概括该软件:SymmetricDS是一个文件和数据库同步软件,开源的,支持多主复制,同步时过滤和在异构的网络环境中进行数据转换传输.它支持单向和双向上的多个订阅者,异步的数据复制. 以下是从CS ...

  7. Untracked files不想add

    $ git status On branch feature/20160420_complain_630222 Untracked files: (use "git add <file ...

  8. 【Xamarin报错】libpng warning : iCCP: Not recognizing known sRGB profile that has been edited

    报错: Xamarin Android 编译时发生以下错误: libpng warning : iCCP: Not recognizing known sRGB profile that has be ...

  9. POJ 1631 Bridging signals

    Bridging signals Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 9441   Accepted: 5166 ...

  10. 非常简单实用的Python HTTP服务

    在做分布式系统应用的时候经常在测试环境上传一个包,或者干嘛的,公司的服务器比较bug,只给ldap权限,每次只能scp到自己的个人目录下,然后才能进到公共账号下去cp,比较麻烦.这时候如果你需要一个简 ...