什么时候应该抛出异样

当一个类型的口头成员不能正在残缺口头工作时,就应该抛出异样告诉调用者。

*口头成员指类型自身或者类型实例能够执行的操作,如C#中StringBuilder中定义的Append,Insert等

捕获异样代码构造
private void DoSomething(){    try    {        //将可能产生异样的代码放在这里    }    catch (InvalidOperationException)    {        //捕捉到InvalidOperationException异样,对应的解决代码放在这里    }    catch (IOException)    {        //捕捉到IOException异样,对应的解决代码放在这里    }    catch (Exception)    {        //捕捉到除了上述之外的其它异样,对应的解决代码放在这里        //这里是将异样抛出        throw;    }    finally    {        //这里的代码总是被执行    }    //如果try块没有抛出异样或者某个catch捕捉到异样却没有抛出就执行以下的代码,否则以下代码不执行}
try块

try块中蕴含的是可能会产生异样的代码,异样复原代码应放在一个或多个catch块中。针对应用程序平安的复原某一种异样都须要有一个对应的catch块。一个try块至多要关联一个catch块或finally块,独自一个try块C#是不容许的,而且这样也没有意义。

*如果一个try块中蕴含执行多个可能抛出同一个异样类型的操作,但不同的操作对应的复原措施不同,咱们就应该将这些操作拆分到它本人的try块中,以保障正确的复原状态

catch块

catch块蕴含的是响应一个异样须要执行的代码。一个try块能够关联0个或多个catch块。如果try块中的代码没有异样产生,CLR永远不会执行catch块中的代码。线程将跳过所有catch块,直至finally块(外面有代码的话)中的代码。catch关键字前面圆括号中的表达式称为捕获类型,即要捕获的异样类型。

*应用Visual Studio调试catch块时,能够在监督窗口中增加非凡的变量名称$exception来查看以后抛出异样的对象

catch块检索程序与注意事项

CLR是自上而下检索一个匹配的catch块,所以编程的时候应留神将派生水平最大的异样类型放在顶部,接着是它们的基类,最初才是System.Exception,如果程序没有放对,例如将最具体的异样类型放在了最底部的catch块中,C#编译器将会产生谬误,因为这个catch块无奈被执行到。

一旦try块中的代码产生异样,而没有与之匹配的catch块的话,CLR会去调用栈的更高一层搜寻与之匹配的异样类型,如果到了栈的顶部还是没有找到,就会产生一个未解决的异样。

在catch块的开端能够做的事件
  • 从新抛出雷同的异样,向调用栈高一层的代码告诉该异样的产生
  • 抛出一个不同的异样,向调用栈高一层的代码提供更加丰盛的异样信息
  • 让线程从catch块的底部退出

前两种技术CLR将回溯调用栈,查找捕获类型与抛出异样的类型匹配的catch块并抛出一个异样。
如果抉择让线程从catch块的底部退出,将立刻执行蕴含在finally块中的代码,结束后执行紧跟在finally块之后的语句。如果不存在finally块,线程将从最初一个catch块之后的语句开始执行。

finally块

finally块中的代码是保障肯定会执行的代码且肯定要在所有catch块的前面,通常蕴含的是对try块中的口头所要求的资源清理操作。一个try块最多只能关联一个finally块。

private void ReadFile(string path){    FileStream fs = null;    try    {        fs = new FileStream(path, FileMode.Open);        //解决文件数据...    }    catch (IOException)    {        //IOException异样复原代码    }    finally    {        //确保文件敞开        if (fs != null)            fs.Close();    }}

上述代码,无论try块代码有没有产生异样,文件都肯定会被敞开。如果将敞开文件的代码放在finally块语句之后是不正确的,因为如果抛出异样但没有捕捉到,finally块之后的语句将永远不会被执行,直到下一次垃圾回收才会敞开文件。

*个别状况下catch块和finally块中的代码应只有一两行

System.Exception类属性介绍
  • 只读属性Message:String类型,指出抛出异样的起因
  • 只读属性Data:IDictionary类型,代码会在抛出异样之前在该汇合中增加一个记录项
  • 可读可写属性Source:String类型,蕴含生成异样的程序集的名称
  • 只读属性StackTrace:String类型,蕴含抛出异样之前调用过的所有办法的名称和签名,有助于调试代码
  • 只读属性TargetSite:MethodBase类型,蕴含抛出异样的办法
  • 只读属性HelpLink:String类型,蕴含异样文档的URL,不倡议应用
  • 只读属性InnerException:Exception类型,通常值为null,如果以后异样是在解决一个异样时抛出的,那么该属性就指出前一个异样是什么
正确应用异样类
  • 善用finally块,罕用于显示开释对象,防止资源泄露
  • 维持状态,产生不可复原的异样时回滚局部实现的操作
  • 在一个线程中捕获异样,在另一个线程中从新抛出异样
  • 正当的从异样中复原状态
private string SomeMothods(){    var result = "";    var a = 1;    var b = 0;    try    {        a /= b;    }    catch (DivideByZeroException)    {        result = "被除数不能为0";    }    return result;}
未解决异样

异样抛出时,CLR会在调用栈中向上查找与抛出异样对象的类型匹配的catch块。如果没有找到,就会产生一个未解决的异样。当CLR检测到过程中的任意一个线程有未解决的异样,都会终止过程。产生未解决异样表明程序遇到了未意料的状况。同时,产生未解决的异样时windows会向事件日志写入一条记录,能够关上事件查看器查看

异样设置
可通过调试菜单关上异样设置窗口如图

开展Common Languages Runtime Exceptions能够查看Visual Studio可能辨认的异样类型

如果勾选了异样类型的复选框,调试器就会在抛出该异样的时候中断,此时CLR不会查找任何与之匹配的catch块。
如果异样类的复选框没有勾选,调试器只有在该异样类型未失去解决时才会中断。

通过增加操作还能够增加自定义的异样类型

异样解决的性能问题
  • 非托管c++编译器:必须生成代码来跟踪哪些对象被结构胜利,编译器还必须生成代码用来在一个异样被捕捉到的时候,调用每一个曾经胜利结构的对象的析构器。如此便会在程序中生成大量的bookkeeping代码,对代码的大小和执行工夫都会造成负面影响。
  • 托管编译器:因为托管对象是在托管堆中调配的,而托管堆受到垃圾回收的监督。如果一个对象胜利结构并且抛出一个异样,垃圾回收器最终会开释对象的内存。编译器无需生成任何bookkeeping代码来跟踪胜利结构的对象,也无需保障析构器的调用。与托管c++相比,意味着生成的代码更少,运行要执行的代码更少,应用程序的性能更好。