编写高质量代码的50条黄金守则-Day 02(首选readonly而不是const)
编写高质量代码的50条黄金守则-Day 02(首选readonly而不是const),本文由比特飞原创发布,转载务必在文章开头附带链接:https://www.byteflying.com/archives/6549
该系列文章由比特飞原创发布,计划用半年时间写完全50篇文章,为大家提供编写高质量代码的一般准则。
1、概述
众所周知,.net 包含 2 种类型的常量,运行时常量和编译时常量,它们的表现行为不同,使用不当,会使你陷入困境。虽然编译时常量在执行上速度略快,但我依然强烈建议大家使用运行时常量(readonly),而不是编译时常量(const)。在继续深入了解之前,我们先要知道 .net 中两种常量各自的特点。
2、.net中两种常量的基本特点
我们看看以下代码片段:
两种常量的代码片段
我们很容易总结出它们各自的特点:
1、运行时常量 readonly 要么在定义的时候赋初始值,要么在构造函数中赋初始值;
2、编译时常量 const 必须在定义的时候赋初始值。
那我为什么建议大家使用运行时常量呢?因为编译时常量可能会使你已发行程序的表现的和你测试时不同,这是为什么呢?为了清楚的了解个中原委,我们要明白编译器为运行时常量和编译时常量都做了什么?
3、编译器为readonly和const关键字做了什么?
使用readonly修饰的常量为运行时常量,使用const修饰的常量为编译时常量,我们先来看两段示例代码:
public static readonly int ViewCount1 = ;
public const int ViewCount2 = ;
我们再来看一下两段代码的IL:
使用 DnSpy 解密的 IL 1
使用 DnSpy 解密的 IL 2
DnSpy 的反编译结果
从上面的反编译结果,可以明显的看出 const 为编译时常量,在编译期间已经被写到IL中。而 readonly 为运行时常量。Microsoft 技术支持文档中相关 IL 的解释也可以印证这一点。
Microsoft 技术支持文档中 ldsfld 的解释
Microsoft 技术支持文档中 Ldc_I4_S 的解释
当然,对于这个案例,输出的结果是一致的:
ViewCount1 和 ViewCount2 的输出结果
于是,我们得到以下重要结论:
1、const 和 readonly 在初始化一次后,不能再赋值;
2、const 在编译时被编译器所替换,而 readonly 在编译时只做校验,不作替换处理,运行时表现如同一般变量。
3、const 在 .net 中只能用于 数值型、字符型和 null,没有例外。
我们再看一个简单的示例:
public const int Birthday= ;
if(DateTime.UtcNow.Year == ) if(DateTime.UtcNow.Year == Birthday)
由我们之前的分析可以得知,它们是完全相同的。然而,并不是所有的情况下,你都能得到你所期望的结果,这又是为什么呢?
4、运行时所给出的结果,并不是你所期望的结果
某些情况下,运行时给出的结果与你所期望的结果大相径庭。现在我们有以下场景,你有一个已交付的软件,软件包含一个可执行主程序 EffectiveCoding02.exe ,一个被主程序所引用的类库 EffectiveCoding02.dll,主程序中包含以下代码:
public class SomeClass{ public static readonly int StartValue = ; public const int EndValue = ; }
类库中包含以下代码:
for(var i = SomeClass.StartValue; i < SomeClass.EndValue; i++){ console.WriteLine($"value = {i}"); }
当初次交付程序给客户的时候,以上代码完全按照你的预期运行。后来由于软件迭代,你修改了主程序:
public class SomeClass{ public static readonly int StartValue = ; public const int EndValue = ; }
重新编译后,因为你只更改了这个主程序,所以你将这个主程序交付给客户,然而你却得不到任何输出内容,程序没有按照你所预期的那样工作,于是你会说出那句开发人员的至理名言,“我这里是正常的啊!”。
产生这个问题的原因在于,由于编译时常量 const 是在编译时被编译器所替换的,所以虽然你进行了编译操作,然而用户计算机中的类库由于没有被编译器重新编译,所以用户计算机中的类库中的 EndValue 还是上一次编译时的结果,即105。然而运行时常量却以正常的方式运作,所以它的值变成了110,自然,你得不到任何的输出结果。因为循环初始值大于结束值。
5、总结
1、运行时常量 readonly 要么在定义的时候赋初始值,要么在构造函数中赋初始值;
2、编译时常量 const 必须在定义的时候赋初始值;
3、const 和 readonly 在初始化一次后,不能再赋值;
4、const 在编译时被编译器所替换,而 readonly 在编译时只做校验,不作替换处理,运行时表现如同一般变量;
5、const 在 .net 中只能用于 数值型、字符型和 null,没有例外;
6、尽量使用 readonly 常量,避免遇到已发行软件无法按预期运行的问题。
开发人员应牢记以上开发守则,否则,人民群众会仇恨你,你的朋友和家人也会嘲笑你、唾弃你。
该系列文章由比特飞原创发布,计划用半年时间写完全50篇文章,为大家提供编写高质量代码的一般准则。
本文由 比特飞 原创发布,欢迎大家踊跃转载。
转载请注明本文地址:https://www.byteflying.com/archives/6549。
编写高质量代码的50条黄金守则-Day 02(首选readonly而不是const)的更多相关文章
- 编写高质量代码的30条黄金守则-Day 01(首选隐式类型转换)
编写高质量代码的30条黄金守则-Day 01(首选隐式类型转换),本文由比特飞原创发布,转载务必在文章开头附带链接:https://www.byteflying.com/archives/6455 该 ...
- 编写高质量代码改善C#程序的157个建议——建议50:在Dispose模式中应区别对待托管资源和非托管资源
建议50:在Dispose模式中应区别对待托管资源和非托管资源 真正资源释放代码的那个虚方法是带一个bool参数的,带这个参数,是因为我们在资源释放时要区别对待托管资源和非托管资源. 提供给调用者调用 ...
- 编写高质量代码:改善Java程序的151个建议(第一章:JAVA开发中通用的方法和准则)
编写高质量代码:改善Java程序的151个建议(第一章:JAVA开发中通用的方法和准则) 目录 建议1: 不要在常量和变量中出现易混淆的字母 建议2: 莫让常量蜕变成变量 建议3: 三元操作符的类型务 ...
- 编写高质量代码改善C#程序的157个建议——建议28:理解延迟求值和主动求值之间的区别
建议28:理解延迟求值和主动求值之间的区别 要理解延迟求值(lazy evaluation)和主动求值(eager evaluation),先看个例子: List<, , , , , , , , ...
- Github即将破百万的PDF:编写高质量代码改善JAVA程序的151个建议
在通往"Java技术殿堂"的路上,本书将为你指点迷津!内容全部由Java编码的最佳 实践组成,从语法.程序设计和架构.工具和框架.编码风格和编程思想等五大方面,对 Java程序员遇 ...
- 《编写高质量代码--Web前端开发修炼之道》读书笔记
前言 这两周参加公司的新项目,采用封闭式开发(项目成员在会议室里开发),晚上加班到很晚,所以没时间和精力写原创博客了,今天就分享下这篇<编写高质量代码--Web前端开发修炼之道>读书笔记吧 ...
- (第一章)改善JavaScript,编写高质量代码。
根据<编写高质量代码改善JavaScript程序的188个建议>这本书,来记录我目前所了解的建议方式. 建议1:警惕Unicode乱码 根据ECMA标准规定JavaScript语言可以使用 ...
- 编写高质量代码改善C#程序的157个建议——建议156:利用特性为应用程序提供多个版本
建议156:利用特性为应用程序提供多个版本 基于如下理由,需要为应用程序提供多个版本: 应用程序有体验版和完整功能版. 应用程序在迭代过程中需要屏蔽一些不成熟的功能. 假设我们的应用程序共有两类功能: ...
- 编写高质量代码改善C#程序的157个建议——建议155:随生产代码一起提交单元测试代码
建议155:随生产代码一起提交单元测试代码 首先提出一个问题:我们害怕修改代码吗?是否曾经无数次面对乱糟糟的代码,下决心进行重构,然后在一个月后的某个周一,却收到来自测试版的报告:新的版本,没有之前的 ...
随机推荐
- vue中引入jq的步骤--以及注意事项
- layui常用插件(二) 时间插件
日期和时间 html <div class="layui-inline"> <!-- 注意:这一层元素并不是必须的 --> <input type=& ...
- 我是如何从零开始自学转行IT并进入世界500强实现薪资翻倍?
本部分内容对应视频链接. 熟悉我的朋友应该知道,我本科及硕士期间所学的专业都是机械相关,毕业两年之后才从零开始自学转行成为一名程序员.当时我写了一篇文章,介绍我的转行经历,很多小伙伴因为我的这篇文章, ...
- SAP四代增强实现:销售订单复制项目文本时不需要显示文本框和回车
最近接收到一个业务需求,在SAP依据销售订单复制时,如果订单里面的项目有多个文本,系统就会显示复制的文本框处理,让用户选择是否复制,这个就让销售很不舒服,如果有几十个项目,每个项目有几个文本,那就就要 ...
- matplotlib绘制子图
fig,subs = plt.subplots(2,2) subs[0][0].plot(data_math_C1) subs[0][0].set_title('C_1 曲线') subs[0][1] ...
- PHP date_modify() 函数
------------恢复内容开始------------ 实例 修改时间戳.增加 15 天: <?php$date=date_create("2013-05-01");d ...
- Go语言系列(三)之数组和切片
<Go语言系列文章> Go语言系列(一)之Go的安装和使用 Go语言系列(二)之基础语法总结 1. 数组 数组用于存储若干个相同类型的变量的集合.数组中每个变量称为数组的元素,每个元素都有 ...
- tensorflow2.0 实现gpu和cpu切换
昨天把GPU版本的tf2.0 安装成功之后,现在所有的代码运行居然都在gpu上跑了,并且在对gpu使用情况没有限制的条件下,既然gpu内存跑满了,代码就崩了怎么样才能随心所欲的指定代码是在cpu还 ...
- .Net Core下基于Emit的打造AOP
之前的基于DispatchProxy的AOP组件,实现了属性注入,但是这个依旧有很多限制 比如不支持构造器注入,继承DispatchProxy的子类必须是公开类 个人有点代码洁癖,不喜欢这种不能控制的 ...
- 【CF600E】Lomset gelral 题解(树上启发式合并)
题目链接 题目大意:给出一颗含有$n$个结点的树,每个节点有一个颜色.求树中每个子树最多的颜色的编号和. ------------------------- 树上启发式合并(dsu on tree). ...