一個類型轉換而引發的三級事件的一些思考

 

  前段時間出了個三級事件,查下來居然是由於一個溢出形成的死循環,在公司出事件仍是挺冒險的一件事,除了大boss要扣錢,還要給web

高層一個合理的解釋,若是在小公司幹活,可能就算網站宕了一天估計也沒事,若是在大點的公司每秒都是銀子的流失,也許形成的損失就算框架

咱們白乾一二年也抵不了,因此責任心和代碼意識真的很重要。性能

     先來看看問題代碼,在這裏我作了一點點的修改,代碼的意思很簡單,就是想獲取參數num中二進制1的個數。測試

 1         static void Run(long num)
 2         {
 3             int i = 1;
 4 
 5             long num2 = 0;
 6 
 7             List<int> list = new List<int>();
 8 
 9             while ((num2 = (long)Math.Pow(2, i - 1)) <= num)
10             {
11                 if ((num & num2) > 0)
12                 {
13                     list.Add(i);
14                 }
15                 i++;
16             }
17         }

若是這是你寫的代碼,你能一眼看出來問題在哪嗎?咱們知道long是8個字節,也就是64位二進制,又由於二進制位中最高位是符號位,因此當網站

是2的63次方時,顯示的就是long的minvalue,因此上面的代碼當i=64的時候,又由於強轉成long,因此最後的結果變成了long的MinValue,spa

循環下去的話就會在負數的道路上越走越遠,而後這個範圍溢出並無被CLR採納,也就沒有給咱們拋出OverflowException,悲劇就這樣.net

無情的發生了,問題是發生了,可是否能從這個問題上有一些思考,在基元類型的強轉中,真的適合用(long),(int)這種強轉模式嗎?從這個例子code

上咱們看到這個(long)模式的強轉根本就不會檢測溢出,因此之後在強轉中最好就不要用這種模式了,由於在.net框架下強轉的方式太多了,在blog

我瞭解的範圍內惟獨這種沒有溢出檢測,可能有些人認爲這種轉換速度是最快的,可是又有多少人能夠信誓旦旦的說個人程序絕對不會有溢出,就接口

算程序有溢所形成行爲異常我也會負全責的?

   爲了推崇非(long)強轉,下面介紹一下其餘的強轉方式。

 

一:爲了更好的理解代碼,咱們先來看看原始不檢測的模式。

1             long i = long.MaxValue;
2             int j = (int)i;

在IL中咱們能夠欣喜的看到這種不檢測溢出的強轉還有專有的IL指令:conv.i4,他的意思就是:將位於計算堆棧頂部的值轉換爲 int32。

 

二:checked

    在咱們學C#語言的那天起,咱們就知道有一個checked,他的惟一做用就是檢測溢出。若是有則拋出異常,那咱們再看看它和無檢測的

方式在IL中有什麼不一樣。

1             long i = long.MaxValue;
2 
3             checked
4             {
5                 int j = (int)i;
6             }

好傢伙,看似大串的代碼在IL中竟然也就一個指令,其實也就多了一個ovf,這個我想你也應該清楚,在轉換的時候多了一個溢出檢測。

若是你從性能上反駁的話,確實這個指令性能必定比無檢測的慢,我想作web的應該是慢的能夠接受。

 

三:Convet.ToXXX。

這個就是C#給咱們專用封裝轉換操做的類,這個也是我寫這篇博客極力推薦的,既然是我推薦的,那確定是會有檢測溢出的,下面咱們來

看看代碼和IL。

1             long i = long.MaxValue;
2 
3             int j = Convert.ToInt32(i);

在IL上咱們看到並無什麼特殊的轉換指令,那判斷確定就在Toint32方法裏面了,下面的目光轉移到它的源代碼中去看一看。

從源代碼中,咱們發現原來代碼如此的簡潔,尤爲是這個if,若是當時用了這個ToIntXXX,也許這個事件就會在測試環境被攔截了,也許某一

天,這個if就是你的最後一根救命稻草。

 

四:IConvertible接口

   在這個接口中封裝了不少類型轉換的方法,並且全部的基元類型都實現了它,不過沒有意思的是居然又調用了下Convert.ToXXX。。。

1             long i = 1;
2 
3             int j = ((IConvertible)i).ToInt32(null);

從上面的IL上能夠看到,竟然有一個box,也難怪IConvertible是引用類型,怎麼可能不box呢?這個接口方法在值類型轉換場景下不值得提

倡,不方便不說,還有較大的性能損失。

 

好了,總結性的話也來了。

① 無檢測代碼模式: 很是不提倡,總有一天會害死你了。

② checked: 這種雖然有檢測,可是寫起來麻煩,固然也能夠在vs裏面本身去設置全局檢測。

③ Convert.Toxxx: 這篇就是爲了提倡它而寫的,因此這個重要性我就不說了,總有一天會救你於水火之中的。

④ IConvertible: 在值類型的場景下,性能最爛並且還很差coding。

相關文章
相關標籤/搜索