好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

详解C#编程中异常的创建和引发以及异常处理

创建和引发异常
异常用于指示在运行程序时发生了错误。此时将创建一个描述错误的异常对象,然后使用 throw 关键字[引发]该对象。然后运行时搜索最兼容的异常处理程序。
当存在下列一种或多种情况时,程序员应引发异常:
方法无法完成其中定义的功能。
例如,如果方法的参数具有无效值:

?

static void CopyObject(SampleClass original)

{

   if (original == null )

   {

     throw new System.ArgumentException( "Parameter cannot be null" , "original" );

   }

 

}

根据对象的状态,对某个对象进行不适当的调用。
一个示例可能尝试对只读文件执行写操作。在对象状态不允许某项操作的情况下,引发 InvalidOperationException 的一个实例或基于此类的派生类的对象。以下为引发 InvalidOperationException 对象的方法的示例:

?

class ProgramLog

{

   System.IO.FileStream logFile = null ;

   void OpenLog(System.IO.FileInfo fileName, System.IO.FileMode mode) {}

 

   void WriteLog()

   {

     if (! this .logFile.CanWrite)

     {

       throw new System.InvalidOperationException( "Logfile cannot be read-only" );

     }

     // Else write data to the log and return.

   }

}

方法的参数导致了异常。
在此情况下,应捕获原始异常并创建一个 ArgumentException 实例。原始异常应作为 InnerException 参数传递给 ArgumentException 的构造函数:

?

static int GetValueFromArray( int [] array, int index)

{

   try

   {

     return array[index];

   }

   catch (System.IndexOutOfRangeException ex)

   {

     System.ArgumentException argEx = new System.ArgumentException( "Index is out of range" , "index" , ex);

     throw argEx;

   }

}

异常包含一个名为 StackTrace 的属性。此字符串包含当前调用堆栈上的方法的名称,以及为每个方法引发异常的位置(文件名和行号)。 StackTrace 对象由公共语言运行时 (CLR) 从 throw 语句点开始自动创建,因此必须从堆栈跟踪的开始点引发异常。
所有异常都包含一个名为 Message 的属性。应该设置此字符串来解释发生异常的原因。注意,不应将安全敏感信息放在消息文本中。除 Message 之外,ArgumentException 还包含一个名为 ParamName 的属性,应将该属性设置为导致引发异常的参数的名称。对于属性设置器,ParamName 应设置为 value。
公共的受保护方法应在其无法完成预期功能时引发异常。引发的异常类应该是符合错误条件的最确切的可用异常。这些异常应编写为类功能的一部分,派生类或对原始类的更新应保留相同的行为,以实现向后兼容性。
引发异常时要避免的情况
下表确定了在引发异常时要避免的做法:

不应使用异常来更改正常执行过程中的程序流程。异常只能用于报告和处理错误条件。 只能引发异常,而不能作为返回值或参数返回异常。 不要从自己的源代码中有意引发 System.Exception、System.SystemException、System.NullReferenceException 或 System.IndexOutOfRangeException。 不要创建可在调试模式下引发但不会在发布模式下引发的异常。若要在开发阶段确定运行时错误,请改用调试断言。

定义异常类

程序可以引发 System 命名空间中的预定义异常类(前面注明的情况除外),或通过从 Exception 派生来创建它们自己的异常类。派生类至少应定义四个构造函数:一个是默认构造函数,一个用来设置消息属性,一个用来设置 Message 属性和 InnerException 属性。第四个构造函数用于序列化异常。新异常类应该可序列化。例如:

?

public class InvalidDepartmentException : System.Exception

{

   public InvalidDepartmentException() : base () { }

   public InvalidDepartmentException( string message) : base (message) { }

   public InvalidDepartmentException( string message, System.Exception inner) : base (message, inner) { }

 

   // A constructor is needed for serialization when an

   // exception propagates from a remoting server to the client.

   protected InvalidDepartmentException(System.Runtime.Serialization.SerializationInfo info,

     System.Runtime.Serialization.StreamingContext context) { }

}

仅当新属性提供的数据有助于解决异常时,才应将其添加到异常类。如果向派生的异常类添加了新属性,则应重写 ToString() 以返回添加的信息。

 


异常处理
C# 程序员可使用 try 块对可能受异常影响的代码进行分区。关联的 catch 块用于处理任何结果异常。一个包含代码的 finally 块,无论 try 块中是否引发异常(例如,释放在 try 块中分配的资源),这些代码都会运行。一个 try 块需要一个或多个关联的 catch 块或一个 finally 块,或两者。
以下示例给出了一个 try-catch 语句,一个 try-finally 语句,和一个 try-catch-finally 语句。

?

try

{

   // Code to try goes here.

}

catch (SomeSpecificException ex)

{

   // Code to handle the exception goes here.

   // Only catch exceptions that you know how to handle.

   // Never catch base class System.Exception without

   // rethrowing it at the end of the catch block.

}

 

  try

{

   // Code to try goes here.

}

finally

{

   // Code to execute after the try block goes here.

}

 

  try

{

   // Code to try goes here.

}

catch (SomeSpecificException ex)

{

   // Code to handle the exception goes here.

}

finally

{

   // Code to execute after the try (and possibly catch) blocks

   // goes here.

}

不带有 catch 或 finally 块的 try 块将导致编译器错误。
Catch 块
catch 块可以指定要捕捉的异常的该类型。类型规范称为[异常筛选器]。异常类型应从 Exception 派生出来。一般而言,不会将 Exception 指定为异常筛选器,除非您了解如何处理 try 块中可能引发的所有异常,或者您在 catch 块中包括了 throw 语句。
具有不同异常筛选器的多个 catch 块可以串联在一起。多个 catch 数据块的计算顺序是在代码中从顶部到底部,但是,对于所引发的每个异常,都只执行一个 catch 数据块。与指定的准确类型或其基类最为匹配的第一个 catch 块被执行。如果 catch 块没有指定匹配异常筛选器,则 catch 块就不具有选定的筛选器(如果语句有的话)。需要将带有最具体的(即派生程度最高的)异常类的 catch 块放在最前面。
当下列条件为真时,应该捕捉异常:
对引发异常的原因有具体的了解,并可实现特定的恢复,例如,在捕获 FileNotFoundException 对象时提示用户输入新的文件名。
可以新建一个更具体的异常并引发该异常。

?

int GetInt( int [] array, int index)

{

   try

   {

     return array[index];

   }

   catch (System.IndexOutOfRangeException e)

   {

     throw new System.ArgumentOutOfRangeException(

       "Parameter index is out of range." );

   }

}

希望在将异常传递出去进行额外处理前部分地处理异常。在下面的示例中,catch 块用于在再次引发异常之前,向错误日志添加条目。

?

try

{

   // Try to access a resource.

}

catch (System.UnauthorizedAccessException e)

{

   // Call a custom error logging procedure.

   LogError(e);

   // Re-throw the error.

   throw ;  

}

Finally 块
可以使用 finally 块清理在 try 块中执行的操作。如果存在,finally 块将在最后执行,在 try 块和任何匹配 catch 的块之后执行。不管是否引发异常或者是否找到与异常类型匹配的 catch 块,finally 始终运行。
可以使用 finally 块释放资源(如文件流、数据库连接和图形句柄),而不用等待由运行时中的垃圾回收器来完成对象。

在下面的示例中,使用 finally 块关闭在 try 块中打开的文件。注意,在关闭文件之前要检查该文件句柄的状态。如果 try 块无法打开文件,则文件句柄仍具有值 null,并且 finally 块不会尝试关闭它。或者,如果在 try 块中成功打开该文件,则 finally 块将关闭打开的文件。

?

System.IO.FileStream file = null ;

System.IO.FileInfo fileinfo = new System.IO.FileInfo( "C:\\file.txt" );

try

{

   file = fileinfo.OpenWrite();

   file.WriteByte(0xF);

}

finally

{

   // Check for null because OpenWrite might have failed.

   if (file != null )

   {

     file.Close();

   }

}

dy("nrwz");

查看更多关于详解C#编程中异常的创建和引发以及异常处理的详细内容...

  阅读:83次