学会使用C#异常

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

所有的异常,它们都是从 Exception 派生出来的。这些异常的类型,都会包含详细描述异常的属性。在这里我将自定义了一个新的异常类,其实也可以自定义配置异常的属性(这是可选的),然后我使用 throw 关键字显示引发该对象(即异常)。

/// <summary>
  /// 定义新异常
  /// </summary>
  class MyException : Exception
  {
   public MyException(string msg) { }
  }
  /// <summary>
  /// 抛出新定义的异常
  /// </summary>
  static void ThrowMyExcetion()
  {
   throw new MyException("Sorry, this is test!");
  }

在引发异常之后,运行时程序会检查当前语句确定它是否包含在 try 块中。 如果是的话,就会检查与该 try 块相关联的所有 catch 块,来确定它们是否能够 catch 该异常。  catch 块通常会指定异常类型;如果该 catch 块的类型与异常或它的基类的相同(或匹配),则该 catch 块就能够捕获并处理。

static void Main(string[] args)
  {
   try
   {
    ThrowMyExcetion(); //直接调用抛出异常的方法
   }
   catch (MyException e)
   {
    Console.WriteLine(e);
   }
   Console.Read();
  }

如果引发异常的语句不在 try 块中,或者包含该语句的 try 块没有匹配的 catch 块,运行时将检查调用方法中是否有 try 语句和 catch 块。 运行时将在调用堆栈中继续往上搜索兼容(或匹配)的 catch 块。在找到并执行 catch 块之后,控制权将传递给 catch 块之后的下一个语句。

一个 try 语句可能包含多个 catch 块。 将执行第一个能够处理该异常的 catch 语句;任何后续的 catch 语句都将被忽略,即使它们是兼容的也如此。 因此,在任何情况下都应该按照从最具体(或者派生程度最高)到最不具体这一顺序排列 catch 块。 例如:

static void Main(string[] args)
  {
   StreamWriter sw = null;
   try
   {
    sw = new StreamWriter(@"C:\book\小二和小三的故事.txt");
    sw.Write("You are 250.");
   }
   catch (FileNotFoundException e)
   {
    //将具体的异常放在第一位
    Console.WriteLine(e);
   }
   catch (IOException e)
   {
    //将并不具体的放在相对后面的位置
    Console.WriteLine(e);
   }
   catch (Exception e)
   {
    Console.WriteLine(e);
   }
   finally
   {
    if (sw != null)
    {
     sw.Close();
    }
   }
   Console.Read();
  }

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

static void Main(string[] args)
  {
   FileStream fs = null;
   FileInfo fi = new FileInfo(@"小二和小三的故事.txt");
   try
   {
    fs = fi.OpenWrite();
    fs.WriteByte(0);
   }
   finally
   {
    //记住哦,如果你忘记 close,将会引发 IO 异常!
    //if (fs != null)
    //{
    // fs.Close();
    //}
   }
   try
   {
    fs = fi.OpenWrite();
    fs.WriteByte(1);
    Console.WriteLine("OK!");
   }
   catch (IOException e)
   {
    Console.WriteLine("Fail!");
   }
   Console.Read();
  }

“Fail!”,这是因为上面注释了需要关闭文件流的语句,你可以尝试下去掉注释看看结果,记住哦,IO 操作都应该在结束时释放资源。 

如果 WriteByte(0)(第9行) 引发了异常,那么在没有调用 fs.Close() 的情况下,你在第二个 try 块中尝试重新 OpenWrit() 的代码就会失败,因为此时文件会保持锁定状态。 假如你取消注释,由于会执行 finally 块(即使已引发异常),使得可以正确地关闭文件,从而避免再次引发异常。

如果在引发异常之后没有在调用堆栈上找到相匹配的 catch 块,则会可能会出现下面的情况:

  • 如果异常出现在析构函数中,则中止该析构函数并调用基类的析构函数(如果有)。
  • 如果调用堆栈包含静态构造函数或静态字段初始值设定项,则会引发 TypeInitializationException,并将原始异常分配给新异常的 InnerException 属性。
  • 如果到达线程的开头,将会终止线程。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持我们!

(0)

相关推荐

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

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

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

    异常和异常处理 C# 语言的异常处理功能可帮助您处理程序运行时出现的任何意外或异常情况.异常处理使用 try.catch 和 finally 关键字尝试某些操作,以处理失败情况,尽管这些操作有可能失败,但如果您确定需要这样做,且希望在事后清理资源,就可以尝试这样做.公共语言运行时 (CLR)..NET Framework 或任何第三方库或者应用程序代码都可以生成异常.异常是使用 throw 关键字创建的. 很多情况下,异常可能不是由代码直接调用的方法引发,而是由调用堆栈中位置更靠下的另一个方法所

  • 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#异常处理详解

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

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

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

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

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

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

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

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

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

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

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

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

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

随机推荐