近期開始接觸到在校學生、高校實習生和畢業生,在此說一下筆者對這些徘徊在職場門口的學生一些建議,希望能給這些初學者進入軟體開發行業帶來一些協助,使得畢業生能更順利的進入軟體開發公司開始職場生涯,人生來一個完美的轉彎。
----------------------------------------異常處理結構
開發軟體一定要有足夠的風險意識,認識到商業軟體在各種複雜的情況下運行,必然會出現各種各樣的風險和錯誤,這些風險和錯誤需要進行處理。無視風險和錯誤,假設一切都很和諧是很危險的思想。
主動處理錯誤
程式開發中可以主動處理錯誤和被動處理錯誤,主動處理錯誤就是進行寫代碼進行功能執行前的檢查,最常見也是最有效手段就是在方法體開頭處檢查方法參數是否正確。主動檢查程式運行速度快,而且系統更穩定,而且將風險消滅在萌芽之中,避免後期的錯誤大爆發,因此是優先採用的風險處理方式。[袁永福著作權]
例如下面的代碼就是主動處理錯誤。
public int Div( int a , int b ) { // 檢查參數,若b為0,則必然會爆被0除的錯誤,提前處理錯誤。 if (b == 0) { return 0; } return a / b; } |
這段代碼就是主動的檢查參數是否正確,儘早制止錯誤的發生。
被動異常處理
主動處理錯誤的方式很可能會有遺漏,C#中可以採用異常捕獲機制來被動的處理錯誤。
在說明異常前首先得講講程式的呼叫堆疊的概念。其實基本上所有的程式設計語言都有呼叫堆疊的功能。例如在程式運行時,方法F1調用了方法F2,F2在某個瞬時又調用了方法F3,而方法F3在某個時刻調用了方法F4。則此時刻存在“F1 | F2 | F3 | F4”的呼叫堆疊。如所示,此時程式的代碼呈現為一種洋蔥一樣的按照方法分層的結構,越靠裡層就越是底層代碼。
C#中的異常就是程式模組發生錯誤的資訊,拋出異常就是程式通知系統程式發生錯誤了,這種異常是全域性,從拋出點開始,沿著呼叫堆疊,一層層的向上推動,越往上,影響面越大,若呼叫堆疊上某層代碼能主動捕獲並處理這個異常,該異常就消失掉了,系統就能正常運行,若一直沒有代碼能主動捕獲異常,則該異常就會一直捅到了.NET架構層面。
例如在F1、F2、F3、F4構成的呼叫堆疊中個,如果方法F4內部主動拋出了一個異常,該異常首先拋給了方法F3,若F3不處理異常則該異常又拋給了F2,若F2還是沒有處理則繼續拋給了F1,若F1沒有處理則直接拋給了.NET架構系統本身了。
這有點像上訪,村裡的農民覺得不滿到找村長上訪,村長不處理接著到縣裡上訪,縣裡不處理則到省裡上訪,省裡不處理則最後跑到中央上訪,中央總是以簡單粗暴的方式處理任何上訪的。
不過凡事驚動中央是不好的,.NET架構就是.NET應用程式的中央,.NET架構本身處理沒有被程式處理的異常會彈出如下的程式錯誤對話方塊
對於該對話方塊,若點擊“繼續”按鈕則程式或許還能接著運行,但可能出於一種不穩定的狀態;點擊“退出”按鈕則立即無條件的退出程式,這可能導致資料未儲存。[袁永福著作權]
一旦顯示出系統級錯誤對話方塊,則該程式的穩定性可靠性是非常的差,應當盡量避免這種情況。這就需要在呼叫堆疊中的某些合適層面的方法中添加異常處理來應付底層方法拋出的異常。
在C#中使用“throw”關鍵字拋出以上,以下代碼就示範了拋出異常。
public int F1() { throw new Exception("這裡發生異常了"); } |
這個方法中就使用throw關鍵字主動拋出了一個異常。在C#中所有的異常都是某個底層方法主動拋出來的。
注意方法F1定義為需要返回一個整數數值,若方法體中沒有return語句編譯不通過的,但使用throw語句時可以壓住這個規定。
在C#中使用“try{ }catch{ }finally{ }”結構來處理異常,以下代碼就示範了如何處理異常
public void HandleException(int a, int b) { try { // 進入能捕獲異常的狀態 F1(); } catch (Exception ext) { // 若發生異常,在此捕獲異常,執行這段代碼 System.Windows.Forms.MessageBox.Show(ext.Message); } finally { // 不管有沒有發生異常,本段代碼都運行 Console.WriteLine("完成了處理"); } } |
在關鍵字“try”後面的代碼塊一般放置方法的功能性代碼,此時系統時刻檢查是否有拋出異常,若拋出異常則立即捕獲它,然後跳轉到關鍵字“catch”後面的代碼塊中處理異常。而無論是否發生異常,關鍵字“finally”後面的代碼塊都會執行的,這段代碼一般用於本方法的善後工作,比如釋放在工作過程中申請的重要資源等。Catch塊和finally塊是可選的,也可以寫成“try{ } catch { }”或者“try{ } finally{ }”。[袁永福著作權]
對於“try{ } catch{ }”結構,程式會捕獲異常並處理掉,使得異常不再上訪。
對於“try{ } finally{ }”結構,程式會感知異常,做一些處理,但不會捕獲異常,異常仍然會接著上訪。
使用異常處理,就能讓底層方法拋出的異常被及時的處理掉而不至於一路捅到.NET架構而成為系統級的錯誤。因此開發程式時必須加上合適的異常處理。
由於異常是被動的處理錯誤,而且執行過程消耗了不少資源,因此主動防禦錯誤永遠比被動處理錯誤要好。在實踐中可以考慮將兩者都用上,做好雙保險,這樣程式才能可靠穩定。[袁永福著作權]
using 文法結構
在C#中,關鍵字using出了可以引用命名空間外,還可以使用using結構來實現一定的異常處理。文法結構為“using( ) { }”。例如以下代碼就示範了using結構。
using (System.IO.FileStream stream = new System.IO.FileStream("c:\\a.txt", System.IO.FileMode.Create)) { stream.WriteByte((byte)2); } |
在欄位代碼中,using後面的園括弧中開啟檔案建立了一個檔案流對象,然後再後面的花括弧的代碼塊中向這個檔案輸出一個位元組,然後就沒有其他代碼了。
檔案流是一種很重要的資源,開啟後必須被關閉,否則就會資源流失,在上面的代碼中應該補上代碼“stream.Close( )”來關閉檔案流。但實際上面的代碼是安全的,不會出現資源流失問題,這是因為採用了using文法結構。
上述代碼其功能等價於以下代碼
System.IO.FileStream stream = new System.IO.FileStream("c:\\a.txt", System.IO.FileMode.Create); try { stream.WriteByte((byte)2); } finally { ( (System.IDisposable)stream).Dispose(); } |
C#編譯器會將using結構翻譯成一個“try{ } finally{ }”結構,並在finally塊中調用檔案流對象的Dispose方法,這個方法就能關閉檔案流,釋放所有的資源。即使其中的功能代碼發生錯誤,拋出異常,檔案流仍然能關閉來釋放資源。
從這裡可以看出using文法結構具有一定的異常處理功能,它沒有捕獲異常,但能感知異常,並進行了一些處理來減少異常帶來的負面影響。
C#中的using文法結構是針對System.IDisposable介面的,System.Idisposable介面只聲明了一個成員“void Dispose( )”,用於釋放對象所佔有的重要資源。任何實現了System.IDisposable介面的對象都能被用上using結構,比如檔案流、資料庫連接、網路連接等等。[袁永福著作權]