using System;
//正确使用异常处理,可以极大地减轻软件开发人员的负担;
//但是,如果使用不当,异常处理可能隐藏代码中的一些严重的问题,也有可能传递一些关于实际问题的错误信息。
//异常处理(exception handling)结构:用4个关键字try、catch、throw和finally使异常问题采用结构化的、统一的和类型安全的方式来进行处理,
// 可以编写出可维护性和健壮性更强的代码,
// 而且使程序代码更清晰、简洁增加了程序的可读性,
// 更重要的是规范了程序的设计风格,有利于提高程序的质量。
// 通过使用异常处理,在编写代码的时候,可以假定这些异常不会发生,只关心编写正确流程的代码。
//当发生某种异常时,CLR就会创建一个表示这个异常的对象,这个对象从具有问题的代码中抛出,异常可以在任何时候抛出。
//在C#中,所有的异常必须由System.Exceptionl类或由从System.Exceptionl类派生的类的异常实例来表示,
//异常:在程序运行期间代码未能完成它应执行的任务时,应抛出一个异常类的对象。
//异常产生的原因: 1)由应用程序本身抛出的异常类的对象,大多数情况下这种异常可以恢复。
// 2)由公共语言运行时环境CLR抛出的异常类的对象,大多数情况下这种异常难以恢复。
//异常产生的方式: 1)由系统抛出自动检测到的异常类的对象。
// (抛出异常的方式) 2)由throw语句抛出人为产生的异常类的对象,
// 应在异常对象中包含一个字符串消息,提供详细信息说明方法为何无法完成任务,并且如果可能,应阐释如何纠正错误。
//异常处理的三种结构:1)try-catch
// 2)try-finally
// 3)try-catch=finally
//异常处理的过程:产生异常---->捕捉异常---->消除异常---->继续执行程序
//异常产生的作用:1)通过catch块中的异常恢复代码,进行异常恢复。
// 2)通过未处理异常(当抛出的异常无法处理时,该异常的消息通常被写入到一个日志中,开发人员可以查看日志,使用消息中的信息来修复代码的bug),查找程序的bug。
public sealed class Program
{
[STAThread]
static void Main(string[] args)
{
//未处理异常:通常会被写入日志文件中。
//如果没有catch块与被抛出的异常类型匹配,会出现未处理异常。
//当CLR检测到进程中的任意线程有未处理异常,会终止进程。
//未处理异常表明应用程序遇到了未预料的情况,而且还是应用程序的真正的bug。
//类库开发人员不必考虑未处理异常,只有应用程序的开发人员需要考虑未处理异常。
//应用程序应设置一种策略来处理未处理异常,这时应将bug报告给发行该应用程序的公司,修改bug,发行一个新版本的应用程序。
//在分布式环境中,理想情况下应用程序发生未处理异常时应记录到日志中,
//然后向客户端发送通知表明所请求的操作无法完成,接着终止服务器应用程序(但在非理想的的环境中不太切合实际,例如SQL Server)。
//Microsoft实际上建议应用程序的开发人员只需接受CLR的默任策略。
//修复未处理异常:
//1.修改代码使catch语句捕捉一个更具体的异常。
//2.重写代码消除导致异常抛出的条件。
Program p = new Program();
p.SomeMethod();
}
private void SomeMethod()
{
try
{
//在这个try块中放入以下要求的代码:
//1)要求执行异常恢复操作:将可能产生异常的程序代码放入try块中。异常恢复代码应该放在一个或多个catch块中。
//2)要求执行公共资源清理操作。资源清理代码应该放在一个单独的finally块中。
}
//CLR自上而下地搜索匹配的catch语句,所以安排catch语句异常处理顺序的一般原则是:
//捕捉具体异常类的catch语句在前面,接着捕捉具体异常类的基类的catch语句在后面,最后是捕捉System.Exception异常类的catch语句。
//1)如果异常被某个catch语句捕捉,则所有其它catch语句都会被忽略,异常从系统中删除。
//2)如果所有catch语句均未捕捉到异常,则异常将被传播到调用try块的上一级(上一级try块或上一级方法)
//3)在捕捉到异常的catch块中,在块的尾部可以有三种选择:
// a)重新抛出相同的异常,则异常将被传播到调用try块的上一级(上一级try块或上一级方法)
// b)抛出一个不同的异常,则异常将被传播到调用try块的上一级(上一级try块或上一级方法)
// a)让线程从catch块的底部退出
//应该为应用程序可以从中恢复的每一种异常都创建一个catch块。catch块中通常包含的是恢复某类异常时需要执行的操作规程代码。
//带参数的catch语句捕捉异常:当抛出的异常对象的类型是catch语句参数类型的同类或派生类时,异常被捕捉。
// catch语句中的异常变量将引用抛出的异常对象,应该把它当成只读变量,尽量不要修改该对象。
//参数的类型称为捕捉类型,在C#中必须将捕捉类型指定为System.Exceptionl异常类或者派生自从System.Exceptionl类的异常类。
catch (InvalidCastException e)
{
//把处理try块中可能产生的异常的代码放到catch块中
//此catch块的代码将处理异常并消除异常,或重新抛出异常
//在这个catch块中放入那些能够从InvalidCastException类型的异常
//(或者任何派生自InvalidCastException的异常类型)中恢复的代码。
}
catch (NullReferenceException)
{
//在这个catch块中放入那些能够从NullReferenceException类型的异常
//(或者任何派生自NullReferenceException的异常类型)中恢复的代码。
}
//带Exception参数的catch语句将捕捉所有类型的异常
catch (Exception)
{
//在这个catch块中放入那些能够从任何类型的异常中恢复的代码。
//捕捉到的任何一个异常时,都应该将其重新抛出,否则、异常处理可能隐藏代码中的一些严重的问题。
//如果捕捉了这个异常,然后又忽略它并继续执行应用程序,应用程序可能产生无法预料的结果,以及安全漏洞。
throw;
}
//不带参数的catch语句将捕捉所有类型的异常
catch
{
//在这个catch块中放入那些能够从任何类型的异常中恢复的代码。
//捕捉到的任何一个异常时,都应该将其重新抛出,否则、异常处理可能隐藏代码中的一些严重的问题。
//如果捕捉了这个异常,然后又忽略它并继续执行应用程序,应用程序可能产生无法预料的结果/行为,以及安全漏洞(安全脆弱性)。
throw;
}
finally
{
//在finally块中包含的代码不管try块中或catch块中是否产生异常都是确保要执行的代码。
//通常放入对try块中的动作所需资源进行清理操作的代码,以确保资源清理代码一定得到执行。
//应该避免将可能抛出异常的代码放在finally块中。
//如果在finally块中抛出异常,则将屏蔽掉try块中或catch块中所产生的异常,并将finally块内的异常传播到调用try块的上一级(上一级try块或上一级方法)
}
//如果try块没有抛出异常,或者某个catch块捕捉了异常且没有抛出别的异常或重新抛出捕捉到的异常,那末就执行finally块后面的代码。
}
}
//throw语句:throw [异常类的对象]
// 1)如果在函数内部,则引发函数内部的异常
// 2)如果在try块中,则引发try块内的异常
// 3)如果在catch块中,使用throw语句可以重新抛出已捕获的异常类的对象,使用throw new异常类()语句可以抛出新的异常类的对象,
// 则引发catch块内的异常,并将异常对象传播到调用try块的上一级(上一级try块或上一级方法)
// 4)如果在finally块中,则引发finally块内的异常,同时屏蔽掉try块中或catch块中所产生的异常,并将finally块内的异常对象传播到调用try块的上一级(上一级try块或上一级方法)
//返回值方式的异常处理:程序调用没有统一的固定标准,有的使用HRESULT,有的使用bool,还有使用其它方式的。
//必须在异常发生的地方捕捉或者检测它们。
//传统的错误处理一直是影响程序设计质量的一个瓶颈。
//bool success = CallFunction();
//if(!success)
//{
//错误处理
//}
//由于没有经验或疏忽,只写如下代码:所有的异常都会被直接抛弃,就产生了bug。
//CallFunction();
//类库开发人员正确地使用异常,应该严格遵循的指导原则:
//1.(方法的参数)验证方法的参数:
// 1)在重用类中实现方法时,在方法的入口点处应验证它的参数,而且对于任何无效的参数,方法应抛出一个派生自System.ArgumentException的异常类型。
// 从System.ArgumentException派生的最有用的异常类型就是System.ArgumentNullException、System.ArgumentOutOfRangeException、
// System.ArgumentDuplicateWaitObjectException。如果未使用这三个异常类型,则可定义派生自System.ArgumentException的异常类型,
// 或者简单地抛出一个System.ArgumentException异常对象。
// 2)当方法接受引用类型的参数时,参数引用的实际对象可以被方法外的代码改变(例如在多线程中)。为了构建一个安全的、健壮的类库,
// 接受引用类型参数的方法应复制这些参数,接着在验证这些参数的副本,最后在方法内部使用该副本。
//2.(finally块)合理使用finally块:清理成功启动的操作,显式地释放对象以避免资源泄漏。
//3.(catch语句)避免捕捉所有的异常:
// 1)捕捉一个异常时,表明该异常是我们能预料到的异常,能理解它为什么发生,并且知道如何修复处理它。
// 2)如果类型是类库的一部分,绝对不应该捕捉并忽略所有的异常,因为不可能知道应用程序如何处理这些异常。
// 3)如果类库的代码捕捉并忽略该异常,应用程序就会继续执行,应用程序可能产生无法预料的结果/行为,以及安全漏洞(安全脆弱性)。
// 4)类库的代码不应捕捉System.Exception异常和其它基类异常。如果这样做,则意味着将所有的具体异常类型都转换成了一个单一的异常类型。
// 这会丢失所有有意义的异常类型信息
//
// 5)如果应用程序检测到无法恢复的异常,而允许应用程序继续运行,可能导致无法预测的行为或者安全脆弱性。就不应该抛出异常,相反应用程序应该强迫进程立即终止。
//
//4.(catch块)从异常中顺利恢复。
//5.(catch块)当异常无法修复是,可回滚局部完成的操作。
//6.(catch块)