C# 拋出 OverflowException 的時機與 checked、unchecked 關鍵字

C# 拋出 OverflowException 的時機與 checked、unchecked 關鍵字

為了測試 bauann 大所說的:在 Windows7 /x64 平台上,VS2008 的 Debug Mode 是不是怪怪的?,不料挖出了自己觀念不夠清楚的地方,關於 C# 偵測 OverflowException 的條件…。

直接看底下簡化的程式碼:
{
    int maxInt = Int32.MaxValue;
    int two = 2;
    int result = maxInt * two;

    Console.WriteLine("{0} * {1} = {2}\r\n>>>NG!\r\nOverflowException 咧...?",
        maxInt,
        two,
        result);
}
catch (Exception e)
{
    Console.WriteLine("發生例外狀況:{0}", e.Message);
}

Console.ReadKey(true);

將變數 maxInt 指定為整數的最大值 (2,147,483,647),如果再乘以 2,估計將發生溢位而拋出例外,但實際上執行起來不是這樣…正當狐疑之際,噗浪上 bauann 大回應說同樣的邏輯在 VB 會丟出例外,而且還提供了一個連結,是一篇外國人寫的 C# 文章,其中有一段敘述著:
Oddly enough, by default, C# does not throw an exception for numerical overflows or underflows. To have C# check, you must use the "checked"  keyword, or configure your project to check all arithmetic.

奇怪的是預設情況下 C# 不會拋出數值溢位或反向溢位。要讓 C# 檢查,你必須使用 "checked" 關鍵字,或設置你的專案檢查所有運算。

有了第一個說法,接著查閱 MSDN 看看是否屬實,發現以上敘述不夠完整 (我只針對字面敘述理解,不代表原作者立場),事實上是這樣的,若運算式只包含常數(運算元都是常數),例如在前述程式碼中,Int32.MaxValue 直接乘以 2,那在編譯時期就會檢查是否超出型別範圍,若偵測到就會有編譯時期溢位錯誤, 參考底下畫面:

OverflowException_Complie_Time

另一個狀況正如一開始的的程式碼,運算式裡包含變數,這種情況稱之為非常數運算式 (non-constant expression),編譯時期不會偵測溢位,預設情況下,執行時期也不會檢查,要強制執行檢查最直接的方式是用 checked 關鍵字,例如修改原本程式碼,第 5 行加入 checked():
{
    int maxInt = Int32.MaxValue;
    int two = 2;
    int result = checked(maxInt * two);

    Console.WriteLine("{0} * {1} = {2}\r\n>>>NG!\r\nOverflowException 咧...?",
        maxInt,
        two,
        result);
}
catch (Exception e)
{
    Console.WriteLine("發生例外狀況:{0}", e.Message);
}

Console.ReadKey(true);

其他還包括專案屬性設定/checked 編譯器選項等方式,至於 unchecked 關鍵字則是用在抑止檢查,假設環境已先被設置為啟用溢位檢查…當然保守的做法是你很確定該程式碼不會有 overflow 發生,這時就可以用 unchecked 去抑止,以期改善一點效能。這些部分由於做法很簡單,想了解的人可以自行參考相關主題連結,不再浪費篇幅介紹了。

題外話,已經不只一次覺得中文版的 MSDN Library 翻譯得實在不怎麼樣,更新日期也比較慢,像這次怎麼看怎麼不懂,有一部分原因是關鍵地方交代不清楚,還要切換到英文版去看(英文版已更新內容,有比較清楚些),個人英文能力又不上不了檯面,還是反覆參考 checked、unchecked 兩篇說明,並且對照範例才真正了解,有時候覺得雖然有自己母語版本的說明文件是很親切沒錯,卻還是得依賴原文說明文件才能了解真正意思,還真夠無言的…。

【2010/1/13 補充】

剛剛逛點部落,發現 alonstar 寫過一篇文章,裡面有使用專案屬性設定方式啟用執行時期溢位檢查的圖文說明,有需要的人不妨過去看看:
[C#]閱讀筆記:indexer, keyword:checked, operator