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 ...
随机推荐
- Android--使用Canvas绘图
前言 除了使用已有的图片之外,Android应用常常需要在运行时根据场景动态生成2D图片,比如手机游戏,这就需要借助于Android2D绘图的支持.本篇博客主要讲解一下Android下使用Canvas ...
- jsp:forward response.sendRedirect
jsp中<jsp:forward page=""/>和response.sendRedirect("")两种跳转的区别 一.response.sen ...
- ubuntu 16.04 source (HUST and 163)
#HUST deb http://mirrors.hust.edu.cn/ubuntu/ xenial main restricted universe multiverse deb http://m ...
- android里TextView加下划线的几种方式
如果是在资源文件里: <resources> <string name="hello"><u>phone:0123456</u>&l ...
- 老王教您怎么做cass7.1 8.0 9.1所有系列的复制狗 加密狗 破解狗
cass7.1 8.0 9.1所有系列的复制狗 加密狗 破解狗本来是出于好奇看到网上有这样的东西,学了下感觉挺简单的,如果你是cass的初学者想仅仅是想学习这个软件,不想花大价格购买正版的,这个是可以 ...
- Win7 安装Apache 2.2.4报错:<OS 5>拒绝访问. :Failed to open the WinNT service manager
Apache 2.2安装档与win7的“用户账户控制”冲突,所以只要先关闭,安装完后再开启即可. 重启,然后再重新安装Apache2.2.4,没有出现错误.然后再把上述设置改回去. 参考:http:/ ...
- System.Net.WebException : The remote server returned an error: (415) UNSUPPORTED MEDIA TYPE
I am having problems with a bit of code that accesses a restful web service. Running this code, it e ...
- Crystal Reports "Access to report file denied. Another program may be using it."
I encounter this problem several times, the way to get around this is to give "Everyone or Netw ...
- C#导出EXCEL的几种方法
using System; using System.Collections.Generic; using System.Text; using System.Data; using System.W ...
- 第一篇HBuilder在这里!
本文作为一个引子,所有相关文章,都会在这里做一个索引,现在还空着.谢谢