C#[C#] C# 知识回顾 – 你真正懂分外(Exception)吗?

您确实懂非凡(Exception)吗?

C# 1

目录

  • 充裕介绍
  • 老大的表征
  • 怎么着使用非常
  • 处理相当的 try-catch-finally
    • 破获非凡的 Catch 块
    • 放活资源的 Finally 块

 

一、异常介绍

  我们一贯在写代码或程序时,无意中(一般就是技巧不够),而造成程序运行时现身意外(又称之为非凡),对于这一个题目,
C# 有特意的相当处理程序(当然其他语言也有)。

  至极处理所涉嫌到的重中之重字有多少个,不用说都晓得,已经深谙了:trycatch 和 finally 等,用来拍卖失利的景观。当然,固然这多少个操作也有可能破产,一般的话是释放,清理某些资源或记录日志等。

  哪些代码会出现相当呢:使用的基类库
BCL、第三方类库和咱们写的自用的代码,还有,可以动用 throw
显式抛出分外。

 

  一种普遍出现的气象,异常很可能不是由代码直接掀起,而是由调用堆栈中更靠下的地点其余模式所掀起。在这种气象下下,CLR
会展开堆栈,并寻找是否有隐含针对你指定特别类型的 catch 块的代码,假诺找到的话,就会实施先河匹配的 catch 块。
假若在调用堆栈中的任意地方中,相当处理程序都尚未找到适当(你写的)的 catch 块,就会活动终止该过程,并向用户体现(抛出)一条错误的信息。

  在这边我写了一个被 0 处会现身相当(会显式引发 DivideByZeroException
相当)的以身作则;虽然在推行的路上出现至极,则捕获该特别。

 1         /// <summary>
 2         /// 除法
 3         /// </summary>
 4         /// <param name="x"></param>
 5         /// <param name="y"></param>
 6         /// <returns></returns>
 7         static double Division(double x, double y)
 8         {
 9             if (y == 0)
10             {
11                 throw new DivideByZeroException();
12             }
13 
14             return x / y;
15         }
16 
17         static void Main(string[] args)
18         {
19             //定义两个变量 x, y
20             double x = 250, y = 0;
21             
22             try
23             {
24                 var result = Division(x, y);
25                 Console.WriteLine($"result: {result}");
26             }
27             catch (DivideByZeroException e)
28             {
29 
30                 Console.WriteLine(e);
31             }
32 
33             Console.Read();
34         }

C# 2

 

二、非凡的特点

  • 有着特别类型(包括自定义的不行)都是由基类 Exception 派生的。

  • 使用 try 块包围你觉得可能会现身异常的代码。

  • 一旦 try 块中生出非常,控制流将按顺序找到与之提到的 catch
    块,如若没有找到适合的,就会掀起最后的十分基类 Exception
    内的处理程序(前提你早就 catch)。“

  • 设若出现相当却没有相应的老大处理程序,则该程序将会告一段落实施,并抛出对应错误的音讯。

  • 在 catch 定义了的可怜变量,可以拿走相应至极类型的音信。比如调用堆栈的情状和错误的证实,具体看
    Excetion 的特性。

  • throw 关键字可以显式引发这个。

  • 即使出现分外也会履行 finally 块中的代码。一般的话,我们会动用 finally 块释放资源,例如,关闭xx流。

 

三、怎么样使用特别

  相当:指的是我们写的次第在运作时出现了错误,并且它会没完没了的蔓延、传播和扩散,有点像病毒一样。

  分外平时由错误的代码引发,可能是用户的错误输入,可能是一方尚未坚守预定来传输格式,也说不定是数据传输的过程中被篡改。我们会对协调觉得有可能报错的代码进行catch ,那称为捕获非常。

  一旦抓住了老大,这一个相当将会在调用堆栈中直接提高拓展传播,直到寻找到跟它异常的 catch 语句。没有
catch
的丰硕会由系统提供的默认的充裕处理程序举办拍卖,也就是你通常来看的一个陡然造成调试中断并出示非常音信的对话框。

C# 3

 

  所有的不得了,它都是从 Exception 派生出来的,他们都带有了详尽的万分描述属性。在这里自己将自定义了一个新的非常类,然后使用 throw 关键字显式引发该对象(即那几个)。 

 1         /// <summary>
 2         /// 定义新异常
 3         /// </summary>
 4         class MyException : Exception
 5         {
 6             public MyException(string msg) { }
 7         }
 8 
 9         /// <summary>
10         /// 抛出新定义的异常
11         /// </summary>
12         static void ThrowMyExcetion()
13         {
14             throw new MyException("Sorry, this is test!");
15         }

 

  在引发那么些之后,CLR
运行时先后会检讨当前说话确定它是否含有在 try 块中。
倘使是的话,就会检查与该 try 块相关联的有着 catch 块,来确定它们是否可以catch
该特别。如果该 catch 块的档次与特别或它的基类的一样(或配合),则该 catch 块就可知捕获并拍卖。

 1         static void Main(string[] args)
 2         {
 3             try
 4             {
 5                 ThrowMyExcetion();  //直接调用抛出异常的方法
 6             }
 7             catch (MyException e)
 8             {
 9                 Console.WriteLine(e);
10             }
11 
12             Console.Read();
13         }

C# 4

 

  倘诺引发那一个的语句不在 try 块中,或者隐含该语句的 try 块没有匹配的 catch 块,CLR
运行时将检查调用方法中是不是有合适 try 语句和 catch 块。
运行时将在调用堆栈中连续往上搜索兼容(或配合)的 catch 块。在找到并实施 catch 块之后,控制权将传递给 catch 块之后的下一个讲话。

  一个 try 语句可能带有两个 catch 块。
将举办第一个可以处理该特另外 catch 语句;任何后续的 catch 语句都将被忽略。
因而,在其他情况下都应该依照从最切实(或者派生程度最高)到最不具体这一顺序排列
catch 块。 例如:

 1         static void Main(string[] args)
 2         {
 3             StreamWriter sw = null;
 4 
 5             try
 6             {
 7                 sw = new StreamWriter(@"C:\book\小二和小三的故事.txt");
 8                 sw.Write("You are 250.");
 9             }
10             catch (FileNotFoundException e)
11             {
12                 //将具体的异常放在第一位
13                 Console.WriteLine(e);
14             }
15             catch (IOException e)
16             {
17                 //将并不具体的放在相对后面的位置
18                 Console.WriteLine(e);
19             }
20             catch (Exception e)
21             {
22                 Console.WriteLine(e);
23             }
24             finally
25             {
26                 if (sw != null)
27                 {
28                     sw.Close();
29                 }
30             }
31 
32             Console.Read();
33         }

 

  执行 catch 块此前,运行时会检查 finally 块。 Finally 块使程序员可以排除中止的 try 块可能遗留下的其它模糊状态,或者释放其他外部资源(例如图形句柄、数据库连接或文件流),而不必等待运行时中的垃圾回收器终结这一个目标。
例如:

 1         static void Main(string[] args)
 2         {
 3             FileStream fs = null;
 4             FileInfo fi = new FileInfo(@"小二和小三的故事.txt");
 5 
 6             try
 7             {
 8                 fs = fi.OpenWrite();
 9                 fs.WriteByte(0);
10             }
11             finally
12             {
13                 //记住哦,如果你忘记 close,将会引发 IO 异常!
14                 //if (fs != null)
15                 //{
16                 //    fs.Close();
17                 //}
18             }
19 
20             try
21             {
22                 fs = fi.OpenWrite();
23                 fs.WriteByte(1);
24                 Console.WriteLine("OK!");
25             }
26             catch (IOException e)
27             {
28                 Console.WriteLine("Fail!");
29             }
30 
31             Console.Read();
32         }

C# 5

 

  “Fail!”,这是因为上边注释了亟需关闭文件流的口舌,你可以品味下去掉注释看看结果,记住哦,IO
操作都应当在终止时释放资源。 

  如果 WriteByte(0)(第9行) 引发了老大,那么在没有调用
fs.Close() 的景色下,你在其次个 try 块中尝试重新 OpenWrit()的代码就会失利,因为这时文件会维持锁定状态。
如若你撤消注释,由于会进行 finally 块(即便已掀起这些),使得可以正确地关闭文件,从而避免再一次抓住那么些。

 

  假如在掀起这么些之后没有在调用堆栈上找到相匹配的 catch 块,则:

  • 若果不行出现在析构函数中,则中止该析构函数并调用基类的析构函数(如果有)。

  • 假设调用堆栈包含静态构造函数或静态字段开首值设定项,则会掀起
    TypeInitializationException,并将原来十分分配给新异常的 InnerException 属性。

  • 假若到达线程的始发,将会终止线程。

 

四、处理异常的 try-catch-finally

  你可以应用 try 块来对你认为可能会出现相当的代码举行分区。
其中,与之提到的 catch 块可用以拍卖其他非常情状。
一个分包代码的 finally 块,无论 try 块中是不是在运作时引发这一个(例如,释放在 try 块中分配的资源),那么些finally 块的代码都会运行。
这一个“分外部分”:可以由一个 try 块、一个或五个事关的 catch 块、一个 finally 块分别结合。

  这里自己列举了 3
种状态:一个 try-catch 语句,一个 try-finally 语句,和一个 try-catch-finally 语句。

  (1)try-catch:

 1         static void Main(string[] args)
 2         {
 3             try
 4             {
 5                 //需要执行的代码
 6             }
 7             catch (Exception e)
 8             {
 9                 //这里可以获取到被捕获的异常
10                 //你需要知道自己应该如何处理该异常
11             }
12         }

 

  (2)try-finally:

1             try
2             {
3                 //需要执行的代码
4             }
5             finally
6             {
7                 //在 try 块后执行的代码
8             }

 

  (3)try-catch-finally:

 1             try
 2             {
 3                 //需要执行的代码
 4             }
 5             catch (Exception e)
 6             {
 7                 //这里处理异常
 8             }
 9             finally
10             {
11                 //在 try 块(也可能是 catch 块)后执行的代码
12             }

  【备注】不带有 catch 或 finally 块的 try 块将促成编译器错误。

 

4.1 捕获相当的 Catch 块

catch 块可以指定要捕捉的不胜类型,又能够叫做“分外筛选器”。
万分类型都是从 Exception 派生出来。 一般而言,不会将具有特此外基类
System.Exception 指定为要 catch
的“非凡筛选器”,除非你异常精晓哪些处理由 try 块引发的有着特别,或者在 catch 块中包括了 throw 语句。

  多个 catch 块可以串联在一齐(要求特别筛选器不同)。
六个 catch 块的施行顺序是:在代码中,从顶部到底层,不过,对于在运作时所掀起的每一个相当,程序都只会执行一个 catch 数据块。
与指定的这一个类型或它的基类相匹配的首先个 catch 块,才会被执行。 经常,我们需要将最特异(最现实或者说派生程度最最最高)的十分类,那段 catch 块放在所有
catch 块的最后边,而她们的基类 Excetion 的 catch
块就坐落最终(当然,也可以不写)。

  在以下标准为真时,你应有拔取 catch 卓殊:

  • 问询引发这一个的原因,并可实现有采取性的还原。例如,在破获 FileNotFoundException
    时您可以唤起用户“文件找不到”和“请输入新的公文名”等。

  • 您也足以新建一个更具象或者说更享有代表性的特别,并精选引发该特别。

    1 double GetNum(double[] nums,int index)
    2 {
    3 try
    4 {
    5 return nums[index];
    6 }
    7 catch (IndexOutOfRangeException e)
    8 {
    9 throw new ArgumentOutOfRangeException(“Sorry, 你想要的目录已经不止界限!”);
    10 }
    11 }

  

  希望在将非凡抛出去时,我们日常会挑选处理部分特别。
在底下这多少个示例中,catch 块在重新 throw 十分以前,添加错误日志。

 1             try
 2             {
 3                 //尝试访问系统资源
 4             }
 5             catch (Exception e)
 6             {
 7                 //伪代码:记录错误日志
 8                 log.Error(e);
 9 
10                 //再重新抛出错误
11                 throw;
12             }

 

4.2 释放资源的 Finally 块

  可以动用 finally 块释放(清理)在 try 块中需要举办释放(清理)资源的操作。
如果存在finally 块,它将在终极执行,也就是在 try 块和任何匹配 catch 块之后执行。
不管是否吸引这么些或者说是否找到与特别类型相匹配的 catch 块,finally 块它始终都会运作。

  可以采纳 finally 块释放资源(如 IO 流、DB
连接和图纸句柄),而不要等待运行时中的垃圾回收器来形成目标资源的回收。
其实,我们更提出利用 using 语句。

  在上边的演示中,我使用 finally 块关闭在 try 块中开拓的公文。注意,在关门文件往日你应有要检查该公文句柄的气象。
假设 try 块不可以打开文件,则文件句柄的值依旧为 null,这时, finally 块就不会尝试关闭它。
或者说,如若在 try 块中打响开拓该文件,则 finally 块才会成功地关闭正在打开的公文。

 1         static void Main(string[] args)
 2         {
 3             FileStream fs = null;
 4             FileInfo fi = new System.IO.FileInfo("C:\\小二和小三的故事.txt");
 5 
 6             try
 7             {
 8                 fs = fi.OpenWrite();
 9                 fs.WriteByte(0);
10             }
11             finally
12             {
13                 // 记得判断 null 哦,不然可能触发其它异常
14                 if (fs != null)
15                 {
16                     fs.Close();
17                 }
18             }
19 
20         }

 

C# 基础回顾系列

  《C# 知识回顾 –
连串化

  《C# 知识回顾 – 表明式树 Expression
Trees

  《C# 知识回顾 – 特性
Attribute
》、《剖析
AssemblyInfo.cs – 了然常用的特征
Attribute

  《C# 知识回顾 – 委托
delegate
》、《C#
知识回顾 – 委托 delegate
(续)

  《C# 知识回顾 –
事件入门
》、《C#
知识回顾 – 伊夫nt
事件

  《string 与 String,大 S 与小 S
之间从未什么不可言说的暧昧

 

 


【博主】反骨仔

【出处】http://www.cnblogs.com/liqingwen/p/6206251.html

【参考】微软官方文档

 

相关文章