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.
}
}