熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

.NET的異常處理的幾個誤區

2022-06-13   來源: .NET編程 

  Net出現多年之後還是對異常處理一知半解的有很多誤解本文將講解三個常見誤解一個是catch的使用方法是否正確另外兩個是try/catch的性能損失問題

  有些人認為下面代碼就是一個catch的錯誤用法

  

  catch(Exception e)
{
throw e;
}

  首先說明這不是一個錯誤用法但是通常來講我們應該避免這種代碼然後要說明的是這段代碼有一個比較典型的作用就是改變異常出現的位置也就是可以對某類異常統一在一個位置處理先看下面代碼

  

  public int GetAllCount()
{
try
{
openDB();
int i = ;
return i;
}
catch (SqlException sex)
{
throw sex;
}
catch (Exception ex)
{
throw ex;
}
}
public int GetAllCount()
{
openDB(); // 這裡也可能是微軟企業類庫等
int i = ;
return i;
}

private void openDB()
{
connOpen();
}

  假設我們有一個公用方法叫openDB()而很多方法中調用它當數據庫打開失敗的時候對於調用GetAllCount方法異常將定位於 connOpen而如果調用GetAllCount那麼異常定位於throw sex的位置同時堆棧信息也有所不同可以更快捷的找到調用方法的位置也可在此位置進行一些錯誤恢復處理尤其是我們編寫一些底層類庫的時候比如 Framework類庫從不會把異常代碼定位到Framework類庫內部的某個方法上面但是需要注意的是我們盡量避免捕獲異常而不返回例如 catch(){}

  這樣的使用就是典型的錯誤使用了因為對於Framework來講任何時候系統都可能拋出一個StackOverflowException或者OutOfMemoryExcetpion而上面這段代碼則隱藏了這些異常有時候則導致一些嚴重的問題

  對於異常處理在性能上有點注意

  第一點在使用try/catch時如果不發生異常那麼幾乎可以忽略性能的損失

  關於這一點這裡我們進行一些深入分析對此比較了解的可以跳過本節首先讓我們先看一下try/catch的IL表現我們有個方法一個使用try/catch而另一個未做任何處理

  

  static int Test(int a int b)
{
try
{
if (a > b)
return a;
return b;
}
catch
{
return ;
}
}

static int Test(int a int b)
{
if (a > b)
return a;
return b;
}

  使用ILDasm工具查看IL代碼分別如下(這裡之所以引入IL是因為IL是比較接近機器匯編所以在IL中我們可以更清楚的了解代碼的執行情況對IL沒有興趣的可以跳過此節)

  

  thod private hidebysig static int Test(int a
int b) cil managed
{
// 代碼大小 (xe)
maxstack
locals init ([] int CS$$
[] bool CS$$)
IL_: nop
try
{
IL_: nop
IL_: ldarg
IL_: ldarg
IL_: cgt
IL_: ldci
IL_: ceq
IL_: stloc
IL_a: ldloc
IL_b: brtrues IL_
IL_d: ldarg
IL_e: stloc
IL_f: leaves IL_b
IL_: ldarg
IL_: stloc
IL_: leaves IL_b
} // end try
catch [mscorlib]SystemObject
{
IL_: pop
IL_: nop
IL_: ldcim
IL_: stloc
IL_: leaves IL_b
} // end handler
IL_b: nop
IL_c: ldloc
IL_d: ret
} // end of method Program::Test

Test

thod private hidebysig static int Test(int a
int b) cil managed
{
// 代碼大小 (x)
maxstack
locals init ([] int CS$$
[] bool CS$$)
IL_: nop
IL_: ldarg
IL_: ldarg
IL_: cgt
IL_: ldci
IL_: ceq
IL_: stloc
IL_: ldloc
IL_a: brtrues IL_
IL_c: ldarg
IL_d: stloc
IL_e: brs IL_
IL_: ldarg
IL_: stloc
IL_: brs IL_
IL_: ldloc
IL_: ret
} // end of method Program::Test

  這裡我們只需關注紅字高亮的幾行即可此處我們只關心try區塊即未發生異常的時候對於Test來講IL代碼多出了個字節來保存catch的處理代碼這一點對性能和資源幾乎是微不足道的

  我們看到當Test執行到IL_f或者IL_的時候將數據出棧並使用leaves退出try區塊轉向IL_b地址然後將數據入棧並返回

  對於Test來講執行到IL_e或者IL_的時候 直接退出並將數據入棧然後返回

  這裡對幾個關鍵指令簡單介紹一下

  

  nop do noting
stloc Pop value from stack into local variable
ldloc Load local variable onto stack
brs target branch to target short form
leaves target Exit a protected region of code short form

  下面我們看代碼的實際運行情況新建一個控制台Console程序加入下面代碼

  

  static void Main(string[] args)
{
int times = ; //我們將結果放大
long l lll s s;

ConsoleWriteLine(Press any key to continue);
ConsoleRead();

for (int j = ; j < ; j++)
{
l = DateTimeNowTicks;

for (int i = ; i < times; i++)
Test( );

l = DateTimeNowTicks;
s = l l;
ConsoleWriteLine(time spent: + s);

l = DateTimeNowTicks;

for (int i = ; i < times; i++)
Test( );

l = DateTimeNowTicks;
s = l l;
ConsoleWriteLine(time spent: + s);
ConsoleWriteLine(difference:+(ss)+rate:+(float)(ss)/s/times); }
}

static int Test(int a int b)
{
try
{
for (int i = ; i < ; i++) ; // 模擬長時操縱
if (a > b)
return a;
return b;
}
catch
{
return ;
}
}

static int Test(int a int b)
{
for (int i = ; i < ; i++) ; // 模擬長時操縱
if (a > b)
return a;
return b;
}

  運行後可以看到代碼的差異通常在%的差別以內

  第二點如果發生異常那麼引發或處理異常時將使用大量的系統資源和執行時間引發異常只是為了處理確實異常的情況而不是為了處理可預知的事件或流控制例如如果方法參數無效而應用程序需要使用有效的參數調用方法則可以引發異常無效的方法參數意味著出現了異常情況相反用戶偶爾會輸入無效數據這是可以預見的因此如果用戶輸入無效則不要引發異常在這種情況下請提供重試機制以便用戶輸入有效輸入

  我們經常需要將一個字符串轉換為int比如將RequestQueryString[id]這樣的字符串轉換為intx時代我們常使用下列方式

  

  try
{
int id = IntParse();
}
catch(){}

  這樣的後果是如果出現轉換異常你將不得不犧牲大量的系統資源來處理異常即使你沒有編寫任何異常處理代碼

  當然你也可以編寫大量的代碼來檢測和轉換字符串來替代try/catch方式而從 以後框架將這個檢測轉換過程封裝到IntTryParse方法中再也不用蹩腳的try/catch來處理了

  還要補充一點就是finally中的代碼是始終保證運行的所以留給大家一個問題下面代碼執行後a的值是多少

  

  int a = ;
try
{
int i = IntParse(s);
}
catch
{
a = ;
return;
}
finally
{
a = ;
}

  小節本文主要對異常處理的個常見誤解進行了糾正撰稿倉促如有疏漏煩請指出


From:http://tw.wingwit.com/Article/program/net/201311/13168.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.