C# checked和unchecked詳解


1、對基元類型執行的許多算術運算都可能造成溢出,有如下代碼:

Byte b=100;
b=(Byte)(b+200);

簡單的解讀上面的代碼:

第一步,將所有的操作數都擴大至32位或者64位(根據操作系統的位數決定)。所以b和200(這兩個值都不超過32位),首先轉換成32位(假設當前操作系統是32位),然后加到一起。結果就是一個32位值(十進制300或者十六進制12C)。注意此時的值為一個32位的操作數,必須轉型為一個byte。C#不會隱式地執行這個轉型操作,這正是第二行代碼需要強制轉換為Byte的原因.如果不把結果值強制轉換為Byte,代碼如下:

byte b = 100;
b = b +300;

如果將值強行轉換為Byte,那么還會出現一個問題,就是值溢出的問題,Byte只能表示0~255范圍的值,所以300超出了Byte的范圍,值就溢出了.不同的語言以不同的方式處理溢出,C和C++不視溢出為錯誤,並允許值回滾.應用程序將若無其事的運行.相反,Microsoft Visual Basic總將溢出視為錯誤,並會在檢測到溢出時拋出一個錯誤.

而CLR提供了一些特殊的IL指令,允許編譯器選擇它認為最正確的行為。CLR有一個add指令,將作用是將兩個值加到一起,但不執行溢出檢查。CLR還有一個add.ovf的指令,作用是將兩個值加到一起,但會在拋出異常時拋出一個System.OverflowException異常。除了用於加法運算的這兩個IL指令外,CLR還為減、乘和數據轉換提供了類似的IL指令,分別是sub/sub.ovf,sub/sub.ovf和conv/conv.ovf。

也就是說C#允許程序員自己決定如何處理溢出,溢出檢查默認是關閉的。因為這樣能保證代碼的運行效率,但是開發人員必須保證不會發生溢出,或者他們的代碼能預見到這些溢出.

 

2、控制溢出的方法

第一種:打開/checked編譯器開關.這個開關指示編譯器在生成代碼時,使用加、減、乘、除和轉換指令的溢出檢查版本也就是帶.vof的版本,這樣,在生成代碼時,就會檢查代碼是否溢出.

下面是/checked編譯器開關的打開方式:

第二種:就是用checked和unchecked關鍵字來控制溢出的檢查與否,這體現的C#溢出檢查的靈活性.

下面是一個在/checked編譯器開關打開的情況下,使用unchecked關鍵字強制不檢查unchecked包裹的代碼的溢出問題,代碼如下:

UInt32 a = unchecked((UInt32)(-1));
Console.WriteLine(a); //一個很大的數

下面在/checked編譯器開關關閉的情況下,使用checked關鍵字檢查其包裹的代碼的溢出問題,代碼如下:

byte b = 100;
b =checked((Byte)(b +300)); //溢出錯誤
Console.WriteLine(b);

 

3、checked和unchecked語句

除了上面的checked和unchecked關鍵字外,checked和unchecked還可以是語句,它們造成一個塊中的表達式就進行/不進行溢出檢查.代碼如下:

checked
{
     byte b = 100; b += 200;//在checked語句塊內,可以直接使用+=操作符,編譯器自動會把值轉換為byte,前提200必須在byte范圍內  Console.WriteLine(b); }

 

4、關於基元類型進行算術操作產生溢出的建議

a、在應用程序能夠容忍checked運算造成的性能損失的情況下,盡可能的打開/checked編譯器開關,保證程序的正常運行

b、盡量使用有符號整數(Int32,Int64),少使用無符號整數(UInt32,UInt64)

c、將不希望發生overflowException的代碼塊作用於checked關鍵字下,並捕獲overflowException,並即時從異常中恢復.

d、c的反例,unchecked的用法.

 


免責聲明!

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



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