Enum引發的血案,反思


前幾天公司產品更新版本,更新完后不少用戶反應原先保存的report的一些表在新版本打開后設置突然變了,本來選的第六個,現在打開變成第四個了。領導要求趕緊查出原因修改好,發緊急補丁。啊啊。。發緊急補丁可是影響team的performance的,年終獎要打折扣了。。

問題是很容易就查到了,那些設置是用Enum表示的,如下:

 1 public enum PeergroupRanks
 2 {
 3     VSBenchmark,
 4  VSBenchmark2,
 5  CalBenchmark,
 6     PeersBeaten,
 7     NumPeergroupBeaten,
 8     PeergroupRank,
 9     NumPeergrouprank,
10     PeergroupPercentile,
11     PeergroupDecile,
12     PeergroupQuintile,
13     PeergroupQuartile,
14 
15     PeergroupRankOfCount,
16 }

一位同事做新feature時加了上面紅色的兩個,由於存report的時候對於這個Enum只是簡單的轉成int存起來,大家都知道Enum默認是從0開始,按順序來,原先存的第6個是PeergroupPercentile,report里存的就是數字5,新加了兩個在上面后,數字5就解析成PeergroupRank了。

分析這個問題,覺得這個應該算是代碼本身有漏洞,同事不小心踩到了,因為這位同事想法也不能說錯,把同一個類型的放到一起,都是Benchmark,代碼可讀性強。

其實項目里大部分代碼對Enum是有所防范的,如:

 1 public enum DisplayBenchmark
 2 {
 3     None,
 4     Benchmark1,
 5     Benchmark2,
 6     CategoryAverage,
 7     CalcBenchmarkId,
 8     CalcBenchmarkType,
 9     CalcBenchmarkCdp,
10 }
11 
12 public static class DisplayBenchmarkCode
13 {
14     const string BENCHMARK1 = "bm1";
15     const string BENCHMARK2 = "bm2";
16     const string CATEGORY = "ca";
17 
18     public static DisplayBenchmark Parse(string code)
19     {
20         switch (code)
21         {
22             case BENCHMARK1:
23                 return DisplayBenchmark.Benchmark1;
24             case BENCHMARK2:
25                 return DisplayBenchmark.Benchmark2;
26             case CATEGORY:
27                 return DisplayBenchmark.CategoryAverage;
28         }
29         return DisplayBenchmark.Benchmark1;
30     }
31 
32     public static string Convert(this DisplayBenchmark type)
33     {
34         switch (type)
35         {
36             case DisplayBenchmark.Benchmark1:
37                 return BENCHMARK1;
38             case DisplayBenchmark.Benchmark2:
39                 return BENCHMARK2;
40             case DisplayBenchmark.CategoryAverage:
41                 return CATEGORY;
42         }
43         return BENCHMARK1;
44     }
45 }

在report里存的是DsiplayBenchmarkType.Convert成的字符串,解析時再Parse,這樣更安全,增加Type的同時也要增加相應的Code,一一對應。

當然,在Enum里寫上具體值也是可行的,如:

1 public enum PeergroupRanks
2 {
3     VSBenchmark=0,
4     VSBenchmark2=1,
5     CalBenchmark=2,
6     PeersBeaten=3,
7 }

還有人覺得直接用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風格也會積極影響所有成員。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM