合并到:https://17bang.ren/Code/304
什么是异常?
Exception:通用规则之外的情况。比如:NullReferenceException,OutOfRangeException……(演示:注意F5和Ctrl+F5的区别)
讨论:(抛)异常 = (有)bug?
事实上,如果代码一跑就有异常,那肯定是bug;但是,有些异常是:
所以,只有把异常抛出来,交由开发/代码维护/……人员处理。比如:掉网了/磁盘满了/断电了……
异常,本来就是那些“正常情况下不会出现”、且“出现了之后‘我’也无法处理”的问题,并且程序不应该继续执行(应该中断)
例如:
抛(throw)出异常
if (score<0 || score > 100) { throw new Exception("成绩只能在0-100分之间"); }
理解:
所以只能抛出异常,交由“调用者”处理
抛出异常,会中断程序执行:这是必须的,以免程序继续执行造成数据错误等
理解:
基类:Exception,常用方法属性:
自定义异常:必须继承自Exception(体会继承)
@想一想@:为什么需要这么多种异常?
try...catch...finally
被抛出的异常,(如果没有被处理/捕获)会直接传递给它的调用者,再由调用者传递给它的调用者,...,直到程序最顶层调用,程序崩溃
为了避免程序崩溃,我们需要try...catch来捕获异常
try //尝试 { SLevel level = Map(101); } catch (FileNotFoundException) //捕获 { //如果是FileNotFoundException //记录到日志,不再抛出 } catch (IndexOutOfRangeException) { //如果是IndexOutOfRangeException //发送Email给维护人员,不再抛出 } catch (Exception) { //其他异常处理 throw; //将异常再次抛出 }
还可以根据不同的异常类型,进行不同的处理。
但注意:越是具体(子类)的异常,越是要放在前面,否则编译无法通过。
finally:无论有无异常(即使有return)都要执行的代码(比如关闭文件流)
使用注意事项
throw:
string n = Console.ReadLine(); try { int i = Convert.ToInt32(n); } catch (FormatException e) { Console.WriteLine("请输入整数……"); }
try...catch:
finally:
示例
几个同学共同开发,完成一个成绩转换等级的代码
//小马负责这个方法 static SLevel Map(int score) { if (score > 100 || score < 0) { //小马完全不知道这个方法会被谁调用 //于是当输入值不符合要求时,直接抛出异常 throw new ArgumentOutOfRangeException(""); } //if (score >= 90) //{ //return SLevel.Perfect; //} else if (score >= 80) { return SLevel.Excellent; } else if (score >= 60) { return SLevel.Passed; } else { return SLevel.Failed; } }
//文轩负责该部分模块 static void Congratulate(int score) { try { SLevel level = Map(score); switch (level) { case SLevel.Excellent: case SLevel.Passed: Console.WriteLine($"恭喜你获得{level}级勋章!"); break; case SLevel.Failed: Console.WriteLine($"很遗憾,你没有通过测试哟!"); break; default: //因为文轩的谨慎,在此后代码发生改动时,及时的暴露了改动造成的问题(演示) throw new NotImplementedException($"输入了{level}级的未处理成绩"); } } catch (Exception e) { //需要记录这些异常信息,看看究竟传入的是什么参数 -- 日志文件 File.AppendAllText("C:\\17bang\\wx-error.log", $"{DateTime.Now}:输入的{score}未能成功转换"/*+ e.ToString()*/+ Environment.NewLine); //但还是无权吞掉(swallow)这个异常,并且认为这是一个无效转换的异常 throw new InvalidCastException($"score(${score})未能成功转换成等级", e); } }
//小龙负责该部分模块 static void InputScore() { //假设没有int.TryParse()方法 //遇到无法转换成整数的情况,就会抛出异常 //小龙也不知道该如何处理,所以不予处理 int score = Convert.ToInt32(Console.ReadLine()); Congratulate(score); }
while (true) { //老程调用InputScore(),发现 //1、该方法可能抛出异常, //2、且不愿让异常导致程序崩溃 //于是只能try...catch进行处理 try { InputScore(); } catch (FormatException e) { Console.WriteLine("你输入的不是一个整数,请重新输入"); Console.WriteLine(); //File.AppendAllText("C:\\17bang\\lc-error.log", e.ToString() + Environment.NewLine); } catch (InvalidCastException e) { Console.WriteLine("你输入的数值不能转换成等级,请重新输入"); Console.WriteLine(); //File.AppendAllText("C:\\17bang\\lc-error.log", e.ToString() + Environment.NewLine); } catch (Exception e) { File.AppendAllText("C:\\17bang\\lc-error.log", e.ToString() + Environment.NewLine); //给用户一个友好提示 Console.WriteLine("程序遇到了未处理的异常情况,请稍后重试或者联系我们……"); Console.WriteLine(); //因为这里是入口(顶级)函数,不需要再throw;了 } }
enum SLevel { //Perfect, Excellent, Passed, Failed }
作业:
多快好省!前端后端,线上线下,名师精讲
更多了解 加: