合并到: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
}
作业:
多快好省!前端后端,线上线下,名师精讲
更多了解 加: