相信有很多人都知道Try-Catch這個東西,只要是寫在Try與Catch裡面的程式發生錯誤,就會跳到Catch的地方,最後就一定會執行finally區段(如果有寫的話).如此方便的做法,使用也相當的普遍,但也常被討論它的效能問題.
相信有很多人都知道Try-Catch這個東西,只要是寫在Try與Catch裡面的程式發生錯誤,就會跳到Catch的地方,最後就一定會執行finally區段(如果有寫的話).如此方便的做法,使用也相當的普遍,但也常被討論它的效能問題,記得早期曾看過一段程式碼,可能當時工程師不知道如何去判斷使用者輸入的文字是否為數字(C#並沒有IsNumeric,但可以用Double.TryParse或Int32.TryParse等的方式去判斷),所以這段程式碼所用的方式是用Try Catch去包一段程式,如果因為非數字而Parse失敗,就跳到Catch去,所以這種效能自然差到不行,這個錯誤的例子是依懶TryCatch來做判斷,需記得TryCatch不是拿來這麼用的,只是該程式並沒有辨法使用TryParse或是Char的IsNumber,因為執行平台是在是.Net1.0,Double也必需到1.1才支援,後續改用其它方式去判斷,雖然程式碼變多,但效能比用TryCatch觸發Exception來的快很多.
TryParse支援版本及參考清單 | ||
型別 | .Net支援(含)版本以上 | 參考 |
Boolean | 2.0 | |
Byte | 2.0 | |
Char | 2.0 | |
DateTime | 2.0 | |
Decimal | 2.0 | |
Double | 1.1 | |
SByte | 2.0 | |
Single | 2.0 | |
IntXX | 2.0 | |
UIntXX | 2.0 | |
備註 | Char可以用IsNumber去判斷是否為數字,.Net1.1以上就可以用了.參考MSDN上的說明. *別誤解TryParse是用來判斷”數值”的.它是用來轉型(Parse)並回傳成功或失敗的結果,以DateTime來說,TryParse就是判斷字串是否轉換日期格式成功. |
TryCatch最常見的賴人寫法就是直接抓通用的Exception,例如 :
Try
{
Code here…..
}
Catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
其實Exception有相當的多種類,例如SqlException、ArgumentNullException等,有興趣的人可以參考MSDN上所提供的資訊,依自己所需要的情況去抓取Exception訊息,例如某段對DB操作的程式碼,如果有發生DB的錯誤,需要將錯誤寫,到Log檔,其它Exception就不需要,就可以用SqlException去抓到DB相關的錯誤.
可是TryCatch因為效能的關係,可別將它一股腦的在程式的每個地方都用TC包起來,如果確定不會出問題的,就不用包了,將TC用在一些難以預期的意外上或是刻意要抓取的例外,就很好用,例如DB的連線,沒人可以保證你的程式在與DB連線時,100%不可能斷線,或是IO寫檔到一半,磁碟機斷線或是掛了,這個程式碼很難防,好不容易寫了一堆程式碼來防範,結果下次又因為別的狀況又掛了,此時還是TryCatch好用.
即然提到它的缺點、類別等,那它在一些特殊用法時,它會是什麼樣的情況,例如巢式TryCatch,呼叫端與被呼叫端均有TC包起來,如果發生Exception,那會是那個TC被觸發?以下用幾個簡單的例子來說明 :
Ex 1 :
try
{
int i = 0;
int k = 10 / i;
try
{
int a = 0;
int b = 10 / a;
}
catch
{
MessageBox.Show("inside");
}
}
catch
{
MessageBox.Show("Out Side");
}
紅色字的部份,就是會發生錯誤的地方,當執行到第一個k=10/i,就會發生錯誤,就會跳到Out Side的catch去,而inside的部份並不會執行到.
Ex 2 :
try
{
try
{
int a = 0;
int b = 10 / a;
}
catch
{
MessageBox.Show("inside");
}
int i = 0;
int k = 10 / i;
}
catch
{
MessageBox.Show("Out Side");
}
Ex2的做法類似Ex1,不過這次把裡面的TC往上移,這次執行就會執行到第二層的TC,所以也會觸發Exception,但只會觸發inside的Exception,OutSide的並不會被觸發,Out Side的部份則是執行到k=10/i時,才會觸發,也就是說,第二層TC觸發後,並不會直接跳到外層的TC,它還是會往下執行.
EX3 :
private void button4_Click(object sender, EventArgs e)
{
try
{
Ex2();
}
catch
{
MessageBox.Show("From click");
}
}
private void Ex2()
{
int i = 0;
int k = 10 / i;
}
呼叫function的程式寫法也很普遍,但如果今天被呼叫的function發生錯誤,而且沒有包TC,用呼叫端去包function時,也是同樣可以catch到錯誤,以這個例子來說,還是可以抓到Message”嘗試以零除。”.
EX4 :
private void button7_Click(object sender, EventArgs e)
{
try
{
Ex5();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void Ex5()
{
try
{
int i = 0;
int k = 10 / i;
}
catch (Exception ex)
{
throw new Exception("Error : "+ex.Message);
}
}
如果有時候希望回丟的訊息除了原本的Message外,能再加上額外的訊息供判斷,例如錯誤單號或是檔案,就能更清楚的找到錯誤資料,這時就可以用throw new Exception的方式來丟資訊,這個例子所Show出來的Message就會是”Error : 嘗試以零除。”
Exception可以抓到的不只是目前例外狀況的訊息而已,也可取得造成錯誤的程式或物件名稱等,finally也是很重要的一環,不管有沒有觸發Exception,finally區段一定都會執行到,所以善用TC及Exception所提供的資訊,相信能更容易的掌握問題點,被Try-Catch-Finally所包住的程式效能並不會有多大的影響,效能只有在觸發Exception時才會比較差,所以該用的時候還是要用,別把TryCatch當文章開頭提的一樣,用來當判斷用,那效能真的是會出人命,而且Exception是可以抓到所有的Exception沒有錯,但效能會差一點,如果能明確指定是那一種Exception,效能會比較好,例如以下寫法.
try
{
Code here.
}
catch (SqlException se)
{
Section 1
}
catch (Exception ex)
{
Section 2
}
如果是發生SqlException相關的錯誤,那就會執行Section 1這段,就不會執行Section 2,如果發生的是SqlException外的,那才會去執行Section 2.
那為什麼明確指定Exception種類的效能會比較好呢,因為它就不用再到共同Excption去一個一個找出錯誤的資訊,減少查詢範圍.
參考資訊 :