C Sharp/Exceptions
外觀
< C Sharp
介紹
[編輯]所有異常對象是System.Exception
類或其子類的實例。.NET Framework定義了很多異常類。程序員可自定義異常類。
2.0版之前要求自定義異常類應該是ApplicationException
異常類的子類。從2.0版開始要求自定義異常類派生自Exception
類[1]。
概況
[編輯]有3種異常處理代碼用法:
try
/catch
- 做事並捕獲可能出現的異常。try
/catch
/finally
- 做事並捕獲可能出現的異常,且總是做finally
。try
/finally
- 做事,且總是做finally
。如果發生異常,會在finally
執行後拋出。
異常捕獲是按照從最特殊到最不特殊的順序。例如,試圖訪問一個不存在的代碼,CLR查詢異常處理器的順序:
FileNotFoundException
IOException
(FileNotFoundException
的基類)SystemException
(IOException
的基類)Exception
(SystemException
的基類)
CLR掃描並查找相匹配的finally子句的過程:開始於引發異常的方法,結束於最頂層的捕獲了異常的方法;在所有「下層」finally子句執行結束之後,相應的catch子句所指定的異常處理代碼塊才開始執行;之後,與此catch子句「同層」的finally子句所指定的異常處理代碼塊得到執行。
例子
[編輯]try
/catch
[編輯]class ExceptionTest
{
public static void Main(string[] args)
{
try
{
Console.WriteLine(args[0]);
Console.WriteLine(args[1]);
Console.WriteLine(args[2]);
Console.WriteLine(args[3]);
Console.WriteLine(args[4]);
}
catch (ArgumentOutOfRangeException e)
{
Console.WriteLine(e.Message);
}
}
}
多個異常捕獲的例子:
class ExceptionTest
{
public static void Main(string[] args)
{
try
{
string fileContents = new StreamReader(@"C:\log.txt").ReadToEnd();
}
catch (UnauthorizedAccessException e) // Access problems
{
Console.WriteLine(e.Message);
}
catch (FileNotFoundException e) // File does not exist
{
Console.WriteLine(e.Message);
}
catch (IOException e) // Some other IO problem.
{
Console.WriteLine(e.Message);
}
}
}
在所有catch
語句中可以忽略異常對象的類型和變量名:
try
{
int number = 1/0;
}
catch (DivideByZeroException)
{
// DivideByZeroException
}
catch
{
// some other exception
}
try
/catch
/finally
[編輯]using System;
class ExceptionTest
{
public static void Main(string[] args)
{
SqlConnection sqlConn = null;
try
{
sqlConn = new SqlConnection ( /*Connection here*/ );
sqlConn.Open();
// Various DB things
// Notice you do not need to explicitly close the connection, as .Dispose() does this for you.
}
catch (SqlException e)
{
Console.WriteLine(e.Message);
}
finally
{
if (sqlConn != null && sqlConn.State != ConnectionState.Closed)
{
sqlConn.Dispose();
}
}
}
}
注意SqlConnection對象在try
/catch
/finally
之外聲明。因為try
/catch
內聲明的變量在finally
內都是不可見的。
try
/finally
[編輯]異常拋出後未能捕獲,則將繼續拋到調用棧。
class ExceptionTest
{
public static void Main(string[] args)
{
SqlConnection sqlConn = null;
try
{
SqlConnection sqlConn = new SqlConnection ( /*Connection here*/ );
sqlConn.Open();
// Various DB bits
}
finally
{
if (sqlConn != null && sqlConn.State != ConnectionState.Closed)
{
sqlConn.Dispose();
}
}
}
}
重新拋出異常
[編輯]如何不拋出異常
[編輯]下述風格代碼是不推薦的:
try
{
// Do something
}
catch (Exception ex)
{
// Ignore this here
}
因為對於OutOfMemoryException
或NullReferenceException
,程序已經無法繼續了。
另一種壞的實踐:
try
{
..
}
catch (Exception ex)
{
throw ex;
}
CLR將認為throw ex;
語句是問題根源,雖然實際上問題出自try塊內。
如何捕獲異常
[編輯]更好的方式是:
/* Read the config file, and return the integer value. If it does not exist, then this is a problem! */
try
{
string value = ConfigurationManager.AppSettings["Timeout"];
if (value == null)
throw new ConfigurationErrorsException("Timeout value is not in the configuration file.");
}
catch (Exception ex )
{
throw; // <-- Throw the existing problem!
}
關鍵字throw;
意味着保持異常信息並拋到上一層調用棧。
異常帶上額外的信息
[編輯]public OrderItem LoadItem(string itemNumber)
{
DataTable dt = null;
try
{
if (itemNumber == null)
throw new ArgumentNullException("Item Number cannot be null","itemNumber");
DataTable dt = DataAccess.OrderItem.Load(itemNumber);
if (dt.Rows == 0)
return null;
else if (dt.Rows > 1)
throw new DuplicateDataException( "Multiple items map to this item.",itemNumber, dt);
OrderItem item = OrderItem.CreateInstanceFromDataRow(dt.Rows[0]);
if (item == null)
throw new ErrorLoadingException("Error loading Item " + itemNumber, itemNumber, dt.Rows[0]);
}
catch (DuplicateDataException dde)
{
throw new ErrorLoadingException("OrderItem.LoadItem failed with Item " +
itemNumber, dde); // <-- Include dde (as the InnerException) parameter
}
catch (Exception ex)
{
throw; // <-- We aren't expecting any other problems, so throw them if they occur.
}
}