前言
錯誤的出現並不總是編寫程式的人的原因,有時應用程式會因為應用程式的終端使用者引發的動作或執行程式碼的環境發生錯誤。無論如何,我們都應預測應用程式中出現的錯誤,並相應的進行編碼。
.Net改進了處理錯誤的方式。C#處理錯誤的機制可以為每種錯誤提供自定義處理方式,並把識別錯誤的程式碼與處理錯誤的程式碼分別開來。
異常類
在C#中當出現某個特殊的異常錯誤條件時,就會建立丟擲一個異常物件,這個物件包含有助於跟蹤問題的資訊。.Net提供了許多預定義的異常類,我們下面看看一些常見特別的異常類吧(異常類太多了,這裡就介紹幾個常見的)。
對於.Net類,一般的異常類System.Exception派生自System.Object,通常不在程式碼中丟擲System.Exception泛型物件,因為他們無法確定錯誤情況的本質。
在該層次中有兩個重要的類,他們派生自System.Exception類:
-
- SystemException------該類用於通常由.NET允許庫丟擲的異常,或者由幾乎所有的應用程式丟擲的異常。例如,如果.NET執行庫檢測到棧已滿,他就會丟擲StackOverflowException異常。另一方面,如果檢測到呼叫方法時引數不對,就可以在自己的程式碼中選擇丟擲ArgumentException異常或其子類。SystemException異常的子類包括表示致命錯誤和非致命錯誤的異常。
- ApplicationException----在.NET Framework最初的設計中,是打算把這個類作為自定義應用程式異常類的基類的。不過,CLR丟擲的一些異常類也派生自這個類。應用程式丟擲的異常則派生自SystemException。因此從ApplicationException派生自自定義異常型別沒有任何好處,取而代之的是,可以直接從Exception基類派生自定義異常類。
其他可能會用到的異常類包括:
-
- StackOverflowException-------如果分配給棧的記憶體區域已滿,就會丟擲這個異常。如果一個方法連續地遞迴呼叫自己,就可能發生棧溢位。這一般是一個致命錯誤,因為它禁止應用程式執行除了中斷以外的其他任務。在這種情況下,甚至也不可能執行到finally塊。通常使用者自己不能處理像這樣的錯誤,而應退出應用程式。
- EndOfStreamException-------這個異常通常是因為讀到檔案末尾而丟擲的,流表示資料來源之間的資料流。
- OverflowException-----如果要在checked上下文中把包含-40的int型別資料強制轉換為uint資料,就會丟擲這個異常
- MemberAccessException----------該類用於處理訪問類的成員失敗時所引發的異常。失敗的原因可能的原因是沒有足夠的訪問許可權,也可能是要訪問的成員根本不存在(類與類之間呼叫時常用)
- IndexOutOfException-------該類用於處理下標超出了陣列長度所引發的異常
使用try...catch...finally捕獲異常
- try 塊包含的程式碼組成了程式的正常操作部分,但這部分程式可能會遇到某些嚴重的錯誤。
- catch塊包含的程式碼處理各種錯誤,這些錯誤是執行try塊中的程式碼時遇到的問題。這個快可以用來記錄錯誤。
- finally快包含的程式碼清理資源或執行通常要在try塊或者catch塊末尾執行的其他操作。無論是否丟擲異常,都會執行finally塊。finally塊中防止return語句,編譯器會標記一個錯誤。另外此塊可以如果沒有需要關閉或者處理的其他操作可以省略此塊。
異常處理具有效能含義,在常見的情況下,不應該使用異常處理錯誤。應儘量編寫好避免錯誤出現的程式碼。
在異常捕獲中,我們可以實現多個catch塊來針對不同的錯誤做出對應的錯誤處理。下面我們看一個例子:
class Program { static void Main(string[] args) { while (true) { try { string userInput; Console.WriteLine("請輸入0-5之間任意一個數字:"); userInput = Console.ReadLine(); if (string.IsNullOrWhiteSpace(userInput)) { break; } if (int.TryParse(userInput, out int index)) { if (index < 0 || index > 5) { throw new IndexOutOfRangeException($"你輸入的數字是{index}"); } Console.WriteLine($"你輸入的數字是{index}"); } else { throw new Exception("請輸入數字"); } } catch (IndexOutOfRangeException ex) { Console.WriteLine($"你輸入的數字不在此範圍內.{ex.Message}"); } catch (Exception ex) { Console.WriteLine(ex.Message); } finally { Console.WriteLine("謝謝合作"); } } } }
在此事例中,定義了兩個catch塊。如果輸入的超過規定返回的數字,則會丟擲超出範圍的錯誤也就進入對應的catch塊。而輸入的非數字也就進入了另外一個catch塊進行處理。
下面我們看一看關於System.Exception屬性。熟悉瞭解其中熟悉能更好的去觀察理解丟擲的異常錯誤。
屬性 |
說明 |
Data |
這個屬性可以給異常新增鍵/值語句,以提供關於異常的額外資訊 |
HelpLink |
連線到一個幫助檔案上,以提供關於該異常的更多資訊 |
InnerException |
如果此異常是在catch塊中丟擲的,它就會包含把程式碼傳送到catch塊的異常物件 |
Message |
描述錯誤情況的文字 |
Source |
導致異常的應用程式名或物件名 |
StackTrace |
棧上方法呼叫的詳細資訊,它有助於跟蹤丟擲異常的方法 |
Hresult |
分配給異常的一個數值 |
TargetSite |
.NET反射物件,描述了丟擲異常的方法 |
過濾異常、建立使用者定義的異常
自從C#6開始就支援異常過濾器。Catch塊僅在過濾器但會true時執行。捕獲不同的異常型別時,可以有行為不同的程式碼塊。在某些情況下,catch塊基於異常的內容執行不同的操作。下面我們看下如何來使用異常過濾器吧:
public class MyIndexOutOfException :SystemException { public MyIndexOutOfException(string message) : base(message) { } public int ErrorCode { get; set; } } class Program { static void Main(string[] args) { try { int steInput = 12; if (steInput > 10) { throw new MyIndexOutOfException("資料超出了範圍") { ErrorCode = 1 }; } } catch (MyIndexOutOfException ex) when (ex.ErrorCode!=1) { Console.WriteLine("出現了自定義錯誤"); } catch (MyIndexOutOfException ex) when (ex.ErrorCode == 1) { Console.WriteLine(ex.Message); } catch (Exception ex) { throw; } } }
上面例子中,自定義了一個異常處理,同事增加ErrorCode,以此啦作為過濾條件,利用關鍵字When+條件來進行過濾。
總結
本篇文章介紹了異常處理錯誤的情況及機制,我們不僅可以輸出程式碼好難過的一般錯誤程式碼,也可以輸出我們自己定義的特殊錯誤情況。無論程式設計技術有多好,程式都必須能處理可能出現的任何錯誤。對不同的錯誤採取相應的應對措施,才是正確編碼的其中一步。
不是井裡沒有水,而是你挖的不夠深。不是成功來得慢,而是你努力的不夠多。
歡迎大家掃描下方二維碼,和我一起學習更多的C#知識