在.NET中,異常是指成員沒有完成它的名稱宣稱可以完成的行動。在異常的機制中,異常和某件事情的發生頻率無關。
異常處理四要素包括:一個表示異常詳細信息的類類型;一個向調用者引發異常類實例的成員;調用者的一段調用異常成員的代碼塊;調用者的一段處理將要發生異常的代碼塊。異常類類型包括:基類:System.Exception;系統級異常:System.SystemException;應用程序級異常:System.ApplicationException。
(一).在.NET中有如下的異常類:
(1).由System.SystemException派生的異常類型:
System.AccessViolationException | 在試圖讀寫受保護內存時引發的異常。 |
System.ArgumentException | 在向方法提供的其中一個參數無效時引發的異常。 |
System.Collections.Generic.KeyNotFoundException | 指定用於訪問集合中元素的鍵與集合中的任何鍵都不匹配時所引發的異常。 |
System.IndexOutOfRangeException | 訪問數組時,因元素索引超出數組邊界而引發的異常。 |
System.InvalidCastException | 因無效類型轉換或顯示轉換引發的異常。 |
System.InvalidOperationException | 當方法調用對於對象的當前狀態無效時引發的異常。 |
System.InvalidProgramException | 當程序包含無效Microsoft中間語言(MSIL)或元數據時引發的異常,這通常表示生成程序的編譯器中有bug。 |
System.IO.IOException | 發生I/O錯誤時引發的異常。 |
System.NotImplementedException | 在無法實現請求的方法或操作時引發的異常。 |
System.NullReferenceException | 嘗試對空對象引用進行操作時引發的異常。 |
System.OutOfMemoryException | 沒有足夠的內存繼續執行程序時引發的異常。 |
System.StackOverflowException | 掛起的方法調用過多而導致執行堆棧溢出時引發的異常。 |
(2).由System.ArgumentException派生的異常類型:
System.ArgumentNullException | 當將空引用傳遞給不接受它作為有效參數的方法時引發的異常。 |
System.ArgumentOutOfRangeException | 當參數值超出調用的方法所定義的允許取值范圍時引發的異常。 |
(3).由System.ArithmeticException派生的異常類型:
System.DivideByZeroException | 試圖用零除整數值或十進制數值時引發的異常。 |
System.NotFiniteNumberException | 當浮點值為正無窮大、負無窮大或非數字(NaN)時引發的異常。 |
System.OverflowException | 在選中的上下文中所進行的算數運算、類型轉換或轉換操作導致溢出時引發的異常。 |
(4).由System.IOException派生的異常類型:
System.IO.DirectoryNotFoundException | 當找不到文件或目錄的一部分時所引發的異常。 |
System.IO.DriveNotFoundException | 當嘗試訪問的驅動器或共享不可用時引發的異常。 |
System.IO.EndOfStreamException | 讀操作試圖超出流的末尾時引發的異常。 |
System.IO.FileLoadException | 當找到托管程序卻不能加載它時引發的異常。 |
System.IO.FileNotFoundException | 試圖訪問磁盤上不存在的文件失敗時引發的異常。 |
System.IO.PathTooLongException | 當路徑名或文件名超過系統定義的最大長度時引發的異常。 |
(5).其他常用異常類型:
ArrayTypeMismatchException | 試圖在數組中存儲錯誤類型的對象。 |
BadImageFormatException | 圖形的格式錯誤。 |
DivideByZeroException | 除零異常。 |
DllNotFoundException | 找不到引用的dll。 |
FormatException | 參數格式錯誤。 |
MethodAccessException | 試圖訪問私有或者受保護的方法。 |
MissingMemberException | 訪問一個無效版本的dll。 |
NotSupportedException | 調用的方法在類中沒有實現。 |
PlatformNotSupportedException | 平台不支持某個特定屬性時拋出該錯誤。 |
(二)..NET的異常處理方式:
發生異常時,系統將搜索可以處理該異常的最近的 catch 子句(根據該異常的運行時類型來確定)。首先,搜索當前的方法以查找一個詞法上包含着它的 try 語句,並按順序考察與該 try 語句相關聯的各個 catch 子句。如果上述操作失敗,則在調用了當前方法的方法中,搜索在詞法上包含着當前方法調用代碼位置的 try 語句。此搜索將一直進行下去,直到找到可以處理當前異常的 catch 子句(該子句指定一個異常類,它與當前引發該異常的運行時類型屬於同一個類或是該運行時類型所屬類的一個基類)。注意,沒有指定異常類的 catch 子句可以處理任何異常。
找到匹配的 catch 子句后,系統將把控制轉移到該 catch 子句的第一條語句。在 catch 子句的執行開始前,系統將首先按順序執行嵌套在捕捉到該異常的 try 語句里面的所有 try 語句所對應的全部 finally 子句。
(1).try塊:包含的代碼通常需要執行一些通用的資源清理操作,或者需要從異常中恢復,或者兩者都需要。try塊還可以包含也許會拋出異常的代碼。
(2).catch塊:包含的是響應一個異常需要執行的代碼。如果沒有任何捕捉類型與拋出的異常匹配,CLR會去調用棧的更高一層搜索一個與異常匹配的捕捉類型。
(3).finally塊:包含的代碼是保證會執行的代碼。finally塊所有代碼執行完畢后,線程退出finally塊,執行緊跟在finally塊之后的語句。
(三).Exception的常用屬性的源碼解析:
(1).Message:包含輔助性文字說明,指出拋出異常的原因。
public virtual String Message {
get {
if (_message == null) {
if (_className==null) {
_className = GetClassName();
}
return Environment.GetRuntimeResourceString("Exception_WasThrown", _className);
} else {
return _message;
}
}
}
(2).Data:對一個“鍵/值對”集合的引用。
public virtual IDictionary Data {
[System.Security.SecuritySafeCritical] // auto-generated
get {
if (_data == null)
if (IsImmutableAgileException(this))
_data = new EmptyReadOnlyDictionaryInternal();
else
_data = new ListDictionaryInternal();
return _data;
}
}
(3).Source:包含生成異常的程序集名稱。
public virtual String Source {
#if FEATURE_CORECLR
[System.Security.SecurityCritical] // auto-generated
#endif
get {
if (_source == null)
{
StackTrace st = new StackTrace(this,true);
if (st.FrameCount>0)
{
StackFrame sf = st.GetFrame(0);
MethodBase method = sf.GetMethod();
Module module = method.Module;
RuntimeModule rtModule = module as RuntimeModule;
if (rtModule == null)
{
System.Reflection.Emit.ModuleBuilder moduleBuilder = module as System.Reflection.Emit.ModuleBuilder;
if (moduleBuilder != null)
rtModule = moduleBuilder.InternalModule;
else
throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeReflectionObject"));
}
_source = rtModule.GetRuntimeAssembly().GetSimpleName();
}
}
return _source;
}
#if FEATURE_CORECLR
[System.Security.SecurityCritical] // auto-generated
#endif
set { _source = value; }
}
(四).異常處理的常用方法:
(1).提取異常及其內部異常堆棧跟蹤
/// <summary>
/// 提取異常及其內部異常堆棧跟蹤
/// </summary>
/// <param name="exception">提取的例外</param>
/// <param name="lastStackTrace">最后提取的堆棧跟蹤(對於遞歸), String.Empty or null</param>
/// <param name="exCount">提取的堆棧數(對於遞歸)</param>
/// <returns>Syste.String</returns>
public static string ExtractAllStackTrace(this Exception exception, string lastStackTrace = null, int exCount = 1)
{
var ex = exception;
const string entryFormat = "#{0}: {1}\r\n{2}";
//修復最后一個堆棧跟蹤參數
lastStackTrace = lastStackTrace ?? string.Empty;
//添加異常的堆棧跟蹤
lastStackTrace += string.Format(entryFormat, exCount, ex.Message, ex.StackTrace);
if (exception.Data.Count > 0)
{
lastStackTrace += "\r\n Data: ";
foreach (var item in exception.Data)
{
var entry = (DictionaryEntry)item;
lastStackTrace += string.Format("\r\n\t{0}: {1}", entry.Key, exception.Data[entry.Key]);
}
}
//遞歸添加內部異常
if ((ex = ex.InnerException) != null)
return ex.ExtractAllStackTrace(string.Format("{0}\r\n\r\n", lastStackTrace), ++exCount);
return lastStackTrace;
}
(2).檢查字符串是空的或空的,並拋出一個異常
/// <summary>
/// 檢查字符串是空的或空的,並拋出一個異常
/// </summary>
/// <param name="val">值測試</param>
/// <param name="paramName">參數檢查名稱</param>
public static void CheckNullOrEmpty(string val, string paramName)
{
if (string.IsNullOrEmpty(val))
throw new ArgumentNullException(paramName, "Value can't be null or empty");
}
(3).檢查參數不是無效,並拋出一個異常
/// <summary>
/// 檢查參數不是無效,並拋出一個異常
/// </summary>
/// <param name="param">檢查值</param>
/// <param name="paramName">參數名稱</param>
public static void CheckNullParam(object param, string paramName)
{
if (param == null)
throw new ArgumentNullException(paramName, paramName + " can't be null");
}
(4).請檢查參數1不同於參數2
/// <summary>
/// 請檢查參數1不同於參數2
/// </summary>
/// <param name="param1">值1測試</param>
/// <param name="param1Name">name of value 1</param>
/// <param name="param2">value 2 to test</param>
/// <param name="param2Name">name of vlaue 2</param>
public static void CheckDifferentsParams(object param1, string param1Name, object param2, string param2Name)
{
if (param1 == param2) {
throw new ArgumentException(param1Name + " can't be the same as " + param2Name,
param1Name + " and " + param2Name);
}
}
(5).檢查一個整數值是正的(0或更大)
/// <summary>
/// 檢查一個整數值是正的(0或更大)
/// </summary>
/// <param name="val">整數測試</param>
public static void PositiveValue(int val)
{
if (val < 0)
throw new ArgumentException("The value must be greater than or equal to 0.");
}
異常處理器(程序):對於程序中出現的異常,在C#中是使用一種被稱為“異常處理器(程序)”的錯誤捕獲機制來進行處理的, 你可以認為異常處理器(程序)就是發生錯誤時,能夠接受並處理錯誤的接受者和處理。
想在異常后繼續執行程序:
1 try 2 { 3 執行的代碼; 4 } 5 catch(Exception ex) 6 { 7 logger.Error(ex);//拋異常后的代碼 8 } 9 finally 10 { 11 需要繼續執行的代碼 12 }