浅谈C#中简单的异常引发与处理操作

异常和异常处理
C# 语言的异常处理功能可帮助您处理程序运行时出现的任何意外或异常情况。异常处理使用 try、catch 和 finally 关键字尝试某些操作,以处理失败情况,尽管这些操作有可能失败,但如果您确定需要这样做,且希望在事后清理资源,就可以尝试这样做。公共语言运行时 (CLR)、.NET Framework 或任何第三方库或者应用程序代码都可以生成异常。异常是使用 throw 关键字创建的。
很多情况下,异常可能不是由代码直接调用的方法引发,而是由调用堆栈中位置更靠下的另一个方法所引发。在这种情况下,CLR 将展开堆栈,查找是否有方法包含针对该特定异常类型的 catch 块,如果找到这样的方法,就会执行找到的第一个这样的 catch 块。如果在调用堆栈中的任何位置都没有找到适当的 catch 块,就会终止该进程,并向用户显示一条消息。
此示例中使用一个方法检测是否有被零除的情况;如果有,则捕获该错误。如果没有异常处理,此程序将终止并产生“DivideByZeroException 未处理”错误。

class ExceptionTest
{
  static double SafeDivision(double x, double y)
  {
    if (y == 0)
      throw new System.DivideByZeroException();
    return x / y;
  }
  static void Main()
  {
    // Input for test purposes. Change the values to see
    // exception handling behavior.
    double a = 98, b = 0;
    double result = 0;

    try
    {
      result = SafeDivision(a, b);
      Console.WriteLine("{0} divided by {1} = {2}", a, b, result);
    }
    catch (DivideByZeroException e)
    {
      Console.WriteLine("Attempted divide by zero.");
    }
  }
}

异常概述
异常具有以下特点:

  • 各种类型的异常最终都是由 System.Exception 派生而来。
  • 在可能引发异常的语句周围使用 try 块。
  • 一旦 try 块中发生异常,控制流将跳转到第一个关联的异常处理程序(无论该处理程序存在于调用堆栈中的什么位置)。在 C# 中,catch 关键字用于定义异常处理程序。
  • 如果给定异常没有异常处理程序,则程序将停止执行,并显示一条错误消息。
  • 除非您可以处理某个异常并使应用程序处于已知状态,否则请不要捕捉该异常。如果捕捉 System.Exception,请在 catch 块的末尾使用 throw 关键字再次引发该异常。
  • 如果 catch 块定义了一个异常变量,则可以用它获取有关所发生异常类型的更多信息。
  • 程序可以使用 throw 关键字显式地引发异常。
  • 异常对象包含有关错误的详细信息,比如调用堆栈的状态以及有关错误的文本说明。
  • 即使发生异常也会执行 finally 块中的代码。使用 finally 块释放资源,例如,关闭在 try 块中打开的任何流或文件。

使用异常
在 C# 中,程序中的运行时错误通过使用一种称为“异常”的机制在程序中传播。 异常由遇到错误的代码引发,由能够更正错误的代码捕捉。 异常可由 .NET Framework 公共语言运行时 (CLR) 或由程序中的代码引发。 一旦引发了一个异常,这个异常就会在调用堆栈中往上传播,直到找到针对它的 catch 语句。 未捕获的异常由系统提供的通用异常处理程序处理,该处理程序会显示一个对话框。
异常由从 Exception 派生的类表示。 此类标识异常的类型,并包含详细描述异常的属性。 引发异常涉及到创建一个异常派生类的实例,配置异常的属性(可选),然后使用 throw 关键字引发该对象。 例如:

class CustomException : Exception
{
  public CustomException(string message)
  {

  }

}
private static void TestThrow()
{
  CustomException ex =
    new CustomException("Custom exception in TestThrow()");

  throw ex;
}

在引发异常之后,运行时检查当前语句以确定它是否在 try 块中。 如果是,则检查与该 try 块关联的任何 catch 块,以确定它们是否能够捕获该异常。 Catch 块通常会指定异常类型;如果该 catch 块的类型与异常或异常的基类的类型相同,则该 catch 块就能够处理该方法。 例如:

static void TestCatch()
{
  try
  {
    TestThrow();
  }
  catch (CustomException ex)
  {
    System.Console.WriteLine(ex.ToString());
  }
}

如果引发异常的语句不在 try 块中,或者包含该语句的 try 块没有匹配的 catch 块,运行时将检查调用方法中是否有 try 语句和 catch 块。 运行时将在调用堆栈中向上继续搜索兼容的 catch 块。 在找到并执行 catch 块之后,控制权将传递给 catch 块之后的下一个语句。
一个 try 语句可能包含多个 catch 块。 将执行第一个能够处理该异常的 catch 语句;任何后续的 catch 语句都将被忽略,即使它们是兼容的也如此。 因此,在任何情况下都应该按照从最具体(或者派生程度最高)到最不具体这一顺序排列 catch 块。 例如:

static void TestCatch2()
{
  System.IO.StreamWriter sw = null;
  try
  {
    sw = new System.IO.StreamWriter(@"C:\test\test.txt");
    sw.WriteLine("Hello");
  }

  catch (System.IO.FileNotFoundException ex)
  {
    // Put the more specific exception first.
    System.Console.WriteLine(ex.ToString());
  }

  catch (System.IO.IOException ex)
  {
    // Put the less specific exception last.
    System.Console.WriteLine(ex.ToString());
  }
  finally
  {
    sw.Close();
  }

  System.Console.WriteLine("Done");
}

执行 catch 块之前,运行时会检查 finally 块。 Finally 块使程序员能够清除中止的 try 块可能遗留下的任何模糊状态,或者释放任何外部资源(例如图形句柄、数据库连接或文件流),而无需等待运行时中的垃圾回收器终结这些对象。 例如:

static void TestFinally()
{
  System.IO.FileStream file = null;
  //Change the path to something that works on your machine.
  System.IO.FileInfo fileInfo = new System.IO.FileInfo(@"C:\file.txt");

  try
  {
    file = fileInfo.OpenWrite();
    file.WriteByte(0xF);
  }
  finally
  {
    // Closing the file allows you to reopen it immediately - otherwise IOException is thrown.
    if (file != null)
    {
      file.Close();
    }
  }

  try
  {
    file = fileInfo.OpenWrite();
    System.Console.WriteLine("OpenWrite() succeeded");
  }
  catch (System.IO.IOException)
  {
    System.Console.WriteLine("OpenWrite() failed");
  }
}

如果 WriteByte() 引发了异常,那么在没有调用 file.Close() 的情况下,第二个 try 块中尝试重新打开文件的代码就会失败,并且文件将保持锁定状态。 由于要执行 finally 块(即使已引发异常),前一示例中的 finally 块使得可以正确地关闭文件,从而帮助避免错误。
如果在引发异常之后没有在调用堆栈上找到兼容的 catch 块,则会出现三种情况中的一种:
如果异常出现在析构函数中,则中止该析构函数并调用基析构函数(如果有)。
如果调用堆栈包含静态构造函数或静态字段初始值设定项,则引发一个 TypeInitializationException,并将原始异常分配给新异常的 InnerException 属性。
如果到达线程的开头,则终止线程。

(0)

相关推荐

  • C#异常处理详解

    异常介绍 1.System.Exception类 Message属性:发生异常的原因和异常的内容 Souce属性:抛出异常程序集的名称 StackTrace属性:发生异常的方法调用情况 InnerException属性:次异常中包含的异常 2.try{}catch{}finally{} 处理异常 a.不带参数的catch和catch(Exception)是有区别的 catch(Exception)可以捕获所有以Exception类派生的异常,而不带参数的catch可以捕获所有异常,不管异常是不是

  • C# WinForm捕获未处理的异常实例解析

    本文以一个完整的实例形式讲述了C# WinForm捕获未处理的异常的方法.分享给大家供大家参考之用.具体代码如下: using System; using System.Collections.Generic; using System.Windows.Forms; using System.IO; namespace GobalException { static class Program { /// <summary> /// 应用程序的主入口点. /// </summary>

  • C#解决SQlite并发异常问题的方法(使用读写锁)

    本文实例讲述了C#解决SQlite并发异常问题的方法.分享给大家供大家参考,具体如下: 使用C#访问sqlite时,常会遇到多线程并发导致SQLITE数据库损坏的问题. SQLite是文件级别的数据库,其锁也是文件级别的:多个线程可以同时读,但是同时只能有一个线程写.Android提供了SqliteOpenHelper类,加入Java的锁机制以便调用.但在C#中未提供类似功能. 作者利用读写锁(ReaderWriterLock),达到了多线程安全访问的目标. using System; usin

  • C#中抛出异常用法实例

    本文实例讲述了C#中抛出异常用法.分享给大家供大家参考.具体如下: C#中可以通过throw抛出一个指定的异常 复制代码 代码如下: private void DoProcess(string value) {   if (string.IsNullOrEmpty(value))   {     throw new ArgumentNullException("value","parameter value cannot be null");   } } 希望本文所

  • 轻松学习C#的异常处理

    异常是程序运行中发生的错误,异常处理是程序设计的一部分.错误的出现并不总是编写应用程序者的原因,有时候应用程序会因为终端用户的操作发生错误.无论如何,在编写程序前,都应该预测应用程序和代码中出现的错误.一般良好的编程规范也会避免一些不必要的程序错误的出现.         在项目的开发过程中,并不是所有的代码执行都和想象那样理想,总是避免不了异常的发生.这就需要编程语言的去处理这些异常,C#语言中有三种异常处理语句:         try...catch;//处理异常         try.

  • 解析C#中断言与异常的应用方式及异常处理的流程控制

    断言与异常(Assertion Vs Exception) 在日常编程实践中,断言与异常的界限不是很明显,这也使得它们常常没有被正确的使用.我也在不断的与这个模糊的怪兽搏斗,仅写此文和大家分享一下我的个人看法.我想我们还可以从很多角度来区别断言和异常的使用场景,欢迎大家的意见和建议. 异常的使用场景:用于捕获外部的可能错误 断言的使用场景:用于捕获内部的不可能错误 我们可以先仔细分析一下我们在.net中已经存在的异常. System.IO.FileLoadException SqlExcepti

  • 解析c#在未出现异常情况下查看当前调用堆栈的解决方法

    C#查看堆栈通常是在异常处理中,出现异常之后通过异常的堆栈可以很方便的得到出现这个错误的代码调用路径.这个很有用,是否可以在没有异常出现时使用这种方法排查一些非异常错误呢?答案是肯定的.起因:论坛发帖子有几个途径,有可能是新闻系统直接导入的帖子,也有可能是抓取的帖子,还有可能是用户通过正常途径发表.但是这两天出了一个问题,有些帖子的HasImage属性不对.通过几种方法做调试都不能重现问题,没有办法,只有在程序中添加回复的地方添加日志程序来记录堆栈,从而追踪到是哪个途径发帖出现了问题.代码: 复

  • 学会使用C#异常

    在 C# 中,程序中在运行时出现的错误,会不断在程序中进行传播,这种机制称为"异常".异常通常由错误的代码引发,并由能够更正错误的代码进行 catch.异常可由 .NET 的 CLR 或由程序中的代码引发.一旦引发了异常,这个异常将会在调用堆栈中一直向上进行传播,直到寻找到跟它匹配的 catch 语句.没有 catch 的异常会由系统提供的默认的异常处理程序进行处理,也就是你经常看到的一个突然造成调试中断并显示异常信息的对话框. 所有的异常,它们都是从 Exception 派生出来的.

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

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

  • c#实现把异常写入日志示例(异常日志)

    将异常写到日志文件中,可以在调试程序的时候知道程序发生过哪些异常,并且可以知道异常发生的位置.这点对需要进行长时间运行并调试的程序尤为有效. 复制代码 代码如下: /// <summary>/// 将异常打印到LOG文件/// </summary>/// <param name="ex">异常</param>/// <param name="LogAddress">日志文件地址</param>p

随机推荐