前几天公司产品更新版本,更新完后不少用户反应原先保存的report的一些表在新版本打开后设置突然变了,本来选的第六个,现在打开变成第四个了。领导要求赶紧查出原因修改好,发紧急补丁。啊啊。。发紧急补丁可是影响team的performance的,年终奖要打折扣了。。

问题是很容易就查到了,那些设置是用Enum表示的,如下:

 public enum PeergroupRanks
{
VSBenchmark,
VSBenchmark2,
CalBenchmark,
PeersBeaten,
NumPeergroupBeaten,
PeergroupRank,
NumPeergrouprank,
PeergroupPercentile,
PeergroupDecile,
PeergroupQuintile,
PeergroupQuartile, PeergroupRankOfCount,
}

一位同事做新feature时加了上面红色的两个,由于存report的时候对于这个Enum只是简单的转成int存起来,大家都知道Enum默认是从0开始,按顺序来,原先存的第6个是PeergroupPercentile,report里存的就是数字5,新加了两个在上面后,数字5就解析成PeergroupRank了。

分析这个问题,觉得这个应该算是代码本身有漏洞,同事不小心踩到了,因为这位同事想法也不能说错,把同一个类型的放到一起,都是Benchmark,代码可读性强。

其实项目里大部分代码对Enum是有所防范的,如:

 public enum DisplayBenchmark
{
None,
Benchmark1,
Benchmark2,
CategoryAverage,
CalcBenchmarkId,
CalcBenchmarkType,
CalcBenchmarkCdp,
} public static class DisplayBenchmarkCode
{
const string BENCHMARK1 = "bm1";
const string BENCHMARK2 = "bm2";
const string CATEGORY = "ca"; public static DisplayBenchmark Parse(string code)
{
switch (code)
{
case BENCHMARK1:
return DisplayBenchmark.Benchmark1;
case BENCHMARK2:
return DisplayBenchmark.Benchmark2;
case CATEGORY:
return DisplayBenchmark.CategoryAverage;
}
return DisplayBenchmark.Benchmark1;
} public static string Convert(this DisplayBenchmark type)
{
switch (type)
{
case DisplayBenchmark.Benchmark1:
return BENCHMARK1;
case DisplayBenchmark.Benchmark2:
return BENCHMARK2;
case DisplayBenchmark.CategoryAverage:
return CATEGORY;
}
return BENCHMARK1;
}
}

在report里存的是DsiplayBenchmarkType.Convert成的字符串,解析时再Parse,这样更安全,增加Type的同时也要增加相应的Code,一一对应。

当然,在Enum里写上具体值也是可行的,如:

 public enum PeergroupRanks
{
VSBenchmark=,
VSBenchmark2=,
CalBenchmark=,
PeersBeaten=,
}

还有人觉得直接用const string就好,个人以为Enum的强类型还是比string好,string的可能性比较多,直接用字符串比较也行,用其他同样string的变量比较也行,没有唯一性,而Enum只能是相同的Type进行比较。

类似的问题的还有hashcode,hashcode会不会变也是依赖于.net framework的算法,谁也不能保证以后算法不会变,所以hashcode也不要做为key存起来,否则后期要改会变得很困难,因为还需要兼容以前存的档案。

另外多语言下的数字也是值得注意的,欧洲那边很多国家的小数点是用逗号表示,分隔符用点号,和我们正好相反,如: 123.456,78  ,这种情况就需要以固定格式存下来,比如ToString时用CultureInfo.InvariantCulture,这样跟区域语言无关,解析时也一样是固定格式解析,double.Parse(value, CultureInfo.InvariantCulture)。显示在界面时就需要用当前的语言格式来显示,总不能给西班牙人看我们常用的小数格式,CultureInfo.CurrentCulture这是当前线程的语言格式,用这个就可以了。

总结起来,要持久化存起来并且需要解析还原的东西是不能变的,保存前是什么状态解析后也要还原这个状态,所以Enum一定要写上值或做转换再存,同样还有hashcode,情愿存长一些的字符串也不要存hashcode(自定义的算法无所谓哈),多语言应用下的小数也需要注意保存和显示的区别。

然后就是上面看到的,同样的项目中绝大部分Enum都做了防范,小部分因为代码规范问题,没能保持一致才出了问题,所以个人觉得这些问题属于基本代码规范问题,在项目设计时就决定好了,每个人不管是老同事还是新进来的同事都需要遵守规范,这样的项目代码更安全,可持续性也更好。

规范的目标是让项目的代码看起来像是一个人写的,团队好的coding风格也会积极影响所有成员。

Enum引发的血案,反思的更多相关文章

  1. [WCF]缺少一行代码引发的血案

    这是今天作项目支持的发现的一个关于WCF的问题,虽然最终我只是添加了一行代码就解决了这个问题,但是整个纠错过程是痛苦的,甚至最终发现这个问题都具有偶然性.具体来说,这是一个关于如何自动为服务接口(契约 ...

  2. dubbox微服务实例及引发的“血案”

    Dubbo 是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成. 主要核心部件: Remoting: 网络通信框架 ...

  3. Integer.parseInt 引发的血案

    Integer.parseInt 处理一个空字符串, 结果出错了, 程序没有注意到,搞了很久, 引发了血案啊!! 最后,终于 观察到了, 最后的部分: Caused by: java.lang.NoC ...

  4. Replication的犄角旮旯(六)-- 一个DDL引发的血案(上)(如何近似估算DDL操作进度)

    <Replication的犄角旮旯>系列导读 Replication的犄角旮旯(一)--变更订阅端表名的应用场景 Replication的犄角旮旯(二)--寻找订阅端丢失的记录 Repli ...

  5. Replication的犄角旮旯(七)-- 一个DDL引发的血案(下)(聊聊logreader的延迟)

    <Replication的犄角旮旯>系列导读 Replication的犄角旮旯(一)--变更订阅端表名的应用场景 Replication的犄角旮旯(二)--寻找订阅端丢失的记录 Repli ...

  6. 转:一个Sqrt函数引发的血案

    转自:http://www.cnblogs.com/pkuoliver/archive/2010/10/06/1844725.html 源码下载地址:http://diducoder.com/sotr ...

  7. 一个Sqrt函数引发的血案(转)

    作者: 码农1946  来源: 博客园  发布时间: 2013-10-09 11:37  阅读: 4556 次  推荐: 41   原文链接   [收藏]   好吧,我承认我标题党了,不过既然你来了, ...

  8. 一个字母引发的血案 java.io.File中mkdir()和mkdirs()

    一个字母引发的血案 明天开始放年假了,临放假前有个爬虫的任务,其中需要把网络图片保存到本地,很简单,马上写完了代码: //省略部分代码... Long fileId= (Long) data.get( ...

  9. form表单提交引发的血案

    最近,公司某条产品线上的一个功能出了问题:点击查询的时候,该页面在IE上直接卡死,chrome上会卡顿一段时间候提交表单进行查询.拿到这个bug单子以后,简单重现了下,基本上定位到是查询操作中的问题, ...

随机推荐

  1. 实现如下类之间的继承关系,并编写Music类来测试这些类。

    实现如下类之间的继承关系,并编写Music类来测试这些类. package com.hanqi.test; public class Instrument { //输出弹奏乐器 public void ...

  2. 使用SQL检测死锁

    第一步:首先创建两个测试表,表goods_sort和goods 表goods_sort:创建并写入测试数据 IF EXISTS(SELECT name FROM sysobjects WHERE na ...

  3. 设计模式C#实现(十二)——装饰模式

    意图 0 适用性 1 结构 2 实现 3 效果 4 参考 5 意图 动态的给一个对象添加一些额外的职责. 适用性 动态的为单个对象添加职责而不影响其他对象 处理那些可以撤销的职责(? 在某些功能不需要 ...

  4. Windows Sever关于80端口之争

    默认情况下安装了IIS服务器角色的Windows系统,其80端口就被占用了.但是占用80端口的进程却不是WWW Service更不是IIS Admin Sevice,而是处于kernel地位的Http ...

  5. 参加 Tokyo Westerns / MMA CTF 2nd 2016 经验与感悟 TWCTF 2016 WriteUp

    洒家近期参加了 Tokyo Westerns / MMA CTF 2nd 2016(TWCTF) 比赛,不得不说国际赛的玩法比国内赛更有玩头,有的题给洒家一种一看就知道怎么做,但是做出来还需要洒家拍一 ...

  6. namenode metadata 备份与恢复实验

    https://hadoop.apache.org/docs/r2.6.0/hadoop-project-dist/hadoop-hdfs/HDFSCommands.html#dfsadmin -me ...

  7. 挤点时间写博客-php&MySQL实践

    hi 晚上要吃火锅的嘛,挤点时间写点东西吧,别被老板发现哦 1.PHP与MySQL 五.文章发布系统之后台 5.2 创建配置文件和初始化文件 为了统一配置以及管理方便,还有就是减少代码的冗余. 分别为 ...

  8. 第16章 调色板管理器_16.4 一个DIB位图库的实现(2)

    //接上一篇 //DibPal.h /*----------------------------------------------------------------- DIBPAL.H heade ...

  9. 第二章《深入C#数据类型》项目经理评分

    一:创建MyOffices项目,创建UserInfo类,用来存储员工 工号,姓名,年龄,评价,年度得分 二:创建查看评分窗体(frmShow),添加定义员工数组,将员工数据绑定到frmShow窗体的L ...

  10. ASP.NET MVC

    ASP.NET MVC 就是根据 Model 2 模式设计的.对于 HTTP 请求的拦截以实现对目标 Controller 和 Action 的解析是通过一个自定义 Http Module 来实现的, ...