您的位置:首页 > 其它

笔记07 异常捕捉与处理

2015-09-23 10:46 197 查看
异常的概念:
在C#中,异常就是发生错误的带名词,即一旦有错误,我们就认为程序发生了异常。
比较专业的解释: 异常实际上是程序中错误导致中断了正常的指令流的一种事件.(你可以认为异常是任何中断正常程序流程的错误条件)。
产生异常的条件:
1:想打开的文件不存在
2:网络连接中断
3:代码错误
。。。。

由于C#是面向对象,所有所有的错误被封装在异常对象中
一旦错误发生,将接收到一个特定的异常对象。
异常和普通问题的区别:
1:在编译上,还是在执行发生。
2:语法错误,还是逻辑错误。
普通问题:编译上,语法错误。
异常:执行,逻辑错误,会产生异常对象。
基本异常
“异常情形”(异常条件)是指引发阻止当前方法或作用域继续执行的问题。
对异常情形和普通问题进行区分是很重要的:
1。所谓的普通问题是指:在当前环境下不能得到足够的信息,比如语法错误,此时错误将会向外传递,而且系统也总能处理这样的错误。
2。而对于异常情形:表示是让程序不能继续执行下去的错误,因为在当前环境下无法获得必要的信息来解决问题。你所能做的是从当前环境跳出,并且把问题提交给处理他的程序,这就是抛出异常时所发生的事情。

当异常发生时,如何处理:
使用:异常处理器(程序):对于程序中出现的异常,在C#中是使用一种被称为“异常处理器(程序)”的错误捕获机制来进行处理的, 你可以认为异常处理器(程序)就是发生错误时,能够接受并处理错误的接受者和处理地!

异常类的分支。
所有的异常类都是Exception的子类
异常类的分类:
1.c#自带的异常类. --->使用SystemException类下的子类
2. 人工创建异常类。-->创建的类一定要继承ApplicationException类,net.2.0技术中,人工创建异常类可以直接继承Exception

SystemException:由.net FrameWork提供并由系统抛出的异常,不过这些异常也可以从你的方法体中抛出。常见的子异常类有:
除数为0时的: DivideByZeroException
在向方法提供的其中一个参数无效时引发的异常:ArgumentException
当试图在数组中存储类型不正确的元素时引发的异常:ArrayTypeMismatchException
。。。。。
属于运行时异常的类型有很多,一般通用的识别他们的规则是:由编程导致的错误。他们会自动被.net 抛出,尽管可以不写,但有时为了在错误后,可以更准确的确定错误的原因,还是应该在程序中显示的抛出此类型的异常,记住:如果出现此类型的异常,则一定是你的程序错误了。

范例:
using System;
using System.Reflection;
using System.Collections;
public class Test3
{
public static void Main(String[] args)
{
DevideTest t = new DevideTest();
t.First = -1;
}
}
public class DevideTest
{
private double first;

public double First
{
get { return first; }
set
{
try
{//try中放置可能发生错误的代码,一旦发生,程序将停止执行,根据错误信息产生异常对象,并且将异常对象传递给类型匹配的catch(),
if (value < 0)
{
throw new Exception("不能给出0一下的数字");
}
else
{

first = value;
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
//Message返回异常对象中包含的错误信息
}
}
}
private double second;

public double Second
{
get { return second; }
set { second = value; }
}

}

System.Exception类的结构和成员。
构建器:
public Exception():初始化Exception类的新实例。
public Exception(string message):使用指定的错误信息初始化 Exception类的新实例。
public Exception(string message, Exception innerException):使用指定错误信息和对作为此异常原因的内部异常的引用来初始化 Exception类的新实例。
属性以及索引器:
public virtual string HelpLink{ get; set; }:获取或设置指向此异常所关联帮助文件的链接的url地址(可以是统一资源名称 (URN) 或统一资源定位器 (URL)),或包含一个解释该异常的网页的引用。
public virtual string Message{ get; }:获取描述当前异常的消息,这个属性是只读的,并且只能调用接收字符串作为参数的构建器来初始化他。
public virtual string Source{get; set;}:获取或设置导致错误的应用程序或对象的名称。
public virtual string StackTrace{get;}:获取当前异常发生时调用堆栈上的帧的字符串表示形式。
范例:
using System;
using System.IO;
public class Test3
{
public static void Main(String[] args)
{
try
{
Exception e = new Exception("发生的错误");
e.Source = "Main()方法发生";
throw e; //将异常对象抛出
Console.WriteLine("ok");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.Source);
Console.WriteLine(e.StackTrace);
}
}
}

如何一段代码中可能抛出异常的类型有很多,如何精确的捕获对应异常?
范例:
using System;
using System.IO;
public class Test3
{
public static void Main(String[] args)
{
try
{
//IndexOutOfRangeException
ArrayTypeMismatchException e = new ArrayTypeMismatchException("发生的错误");
e.Source = "Main()方法发生";
throw e; //将异常对象抛出
//Console.WriteLine("ok");
}
catch(ArrayTypeMismatchException e)
{//一旦匹配,就不再继续向下做匹配的工作,程序会退出整个try{}catch{}语句
Console.WriteLine(e.GetType().ToString());
Console.WriteLine(e.Message);
Console.WriteLine(e.Source);
Console.WriteLine(e.StackTrace);
}
catch(IndexOutOfRangeException e)
{
Console.WriteLine(e.GetType().ToString());
Console.WriteLine(e.Message);
Console.WriteLine(e.Source);
Console.WriteLine(e.StackTrace);
}
catch (Exception e)
{//1.起到最终捕获异常类对象的效果。
//2. 为前面的精确捕获打下铺垫。
Console.WriteLine(e.GetType().ToString());
Console.WriteLine(e.Message);
Console.WriteLine(e.Source);
Console.WriteLine(e.StackTrace);
}
//
}
}

注意:异常处理程序必须紧跟在try块之后,当异常被抛出时,异常处理机制将负责搜寻参数与异常类型类型相匹配的第一处理程序,然后进入catch子句执行,此时认为异常得到了处理,一旦catch子句结束,则处理程序的查找过程结束。注意,只有匹配的catch子句才能得到执行。这与switch语句不同(case语句都有break,以避免执行后续的case子句)。

凡是返回抽象类或接口类型的方法或属性,注意:真实返回的是实现接口或继承乐抽象类子类对象
获取产生异常的方法
public MethodBase TargetSite { get; } :获取引发当前异常的方法,C#将一个方法的所有信息封装在MethodBase类中, 返回是MethodBase的子类的对象。

MethodBase类位于System.Reflection命名空间中。
public abstract class MethodBase : MemberInfo:提供有关方法和构造函数的信息,
范例:
using System;
using System.Reflection;
public class Test3
{
public static void TestException()
{
throw new Exception("报错异常");
//当抛出异常时,先查看当前方法是否有try..catch(),如果有,就就地解决,如果没有,程序将自动将异常带回到方法的调用处,查看是否有try..catch,没有没有,程序将异常对象交给Main()方法,查看Main()是否有try..catch,没有,直接终止程序的运行,将异常抛向控制台
}
public static void Main(string[] args)
{
try
{
TestException();
}
catch (Exception e)
{//接收抛出的所有类型的异常。
MethodBase m = e.TargetSite;
Console.WriteLine("报出异常的方法为:" + m.Name);
Console.WriteLine("方法所属于的类为:" + m.DeclaringType.ToString());
Console.WriteLine("方法是否为static:" + m.IsStatic);
Console.WriteLine("方法是否为公有的:" + m.IsPublic);
Console.WriteLine("方法是否是抽象的:" + m.IsAbstract);
}
}
}

Exception:
SystemException :C#自带的异常类
ApplicationException :用户自定义的异常类的根类。
C#2.0中,自定义的类可以直接继承Exception
注意:
一旦继承乐Exception:
1. 在自定义的异常类中定义三个构建器。
2.覆盖父类的Message属性,在原始的Message属性数值的前面或后面加上一些重要的注释代码

问题:
需要设置一种方式:
不管是否抛出异常,都需要执行某段代码。
使用finally进行清理。
1。对于一些代码,可能会希望无论try块中是否有异常抛出, 最终都能得到执行,比如变量的回收,此时,可以使用finally子句.
2。Finally子句既可和try关键字单独搭配使用,也可以与try..catch语句共同使用。
3。有异常时,在执行了匹配的catch()包含的语句后执行,没有异常时,在try中包含的所有语句执行完后执行)。
4。为了让内存中的变量不管是否发生异常都能达到收回的效果,可以在异常处理程序后加上finally子句。
范例:
using System;
public class Test4
{
public static void Main(string[] args)
{
string name = Console.ReadLine();
try
{
if (name.Trim() == "")
{
TeamCustomException te = new TeamCustomException("员工的姓名不能为null");
te.TeamName = "团队1";
throw te;
}
else
{
Console.WriteLine(name);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
finally
{//无论try中是否有异常抛出,在抛出异常前,执行的代码。
name = null;
Console.WriteLine("ff");
}
}
}
public class TeamCustomException : Exception
{
private string teamName;

public string TeamName
{
get { return teamName; }
set { teamName = value; }
}
public override string Message
{
get
{
return teamName+":"+base.Message;
}
}
public TeamCustomException()
: base()
{//让父类的构建器帮我们初始化父类中定义的所有变量。
logException();
}
public TeamCustomException(string info)
: base("发生的错误信息:" + info)
{
logException();
}
//public CustomException(string info, Exception innerException)
// : base("发生的错误信息:" + info, innerException)
//{
// logException();
//}
private void logException()
{//日志的记录
Console.WriteLine("记录当前发生的错误信息到日志中");
}
}

问题:finally语句中是否可以放置任何的语句呢?
在finally子句中,不能放置break,return等跳出当前控制程序的关键字,否则系统将抛出:控制不能离开 finally 子句主体的错误。

如何在程序中,设计:所有的异常在统一的地方进行捕获?
Main()方法中捕获所有的异常。
解决:异常的嵌套。--异常中包含异常对象。
范例:
using System;
public class Test4
{
public static void Main(string[] args)
{
string name = Console.ReadLine();
try
{
if (name.Trim() == "")
{
TeamCustomException te = new TeamCustomException("员工的姓名不能为null");
te.TeamName = "团队1";
throw new Exception("TeamCustomException", te);
}
else
{
Console.WriteLine(name);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Exception yuan = e.InnerException; //返回异常对象中包含的原始异常类对象。
Console.WriteLine(yuan.Message);
}
finally
{//无论try中是否有异常抛出,在抛出异常前,执行的代码。
name = null;
Console.WriteLine("ff");
}
}
}
public class TeamCustomException : Exception
{
private string teamName;

public string TeamName
{
get { return teamName; }
set { teamName = value; }
}
public override string Message
{
get
{
return teamName+":"+base.Message;
}
}
public TeamCustomException()
: base()
{//让父类的构建器帮我们初始化父类中定义的所有变量。
logException();
}
public TeamCustomException(string info)
: base("发生的错误信息:" + info)
{
logException();
}
//public CustomException(string info, Exception innerException)
// : base("发生的错误信息:" + info, innerException)
//{
// logException();
//}
private void logException()
{//日志的记录
Console.WriteLine("记录当前发生的错误信息到日志中");
}
}

问题:为了统一整个项目中所有的异常类型,要求程序员必须抛出自定义的异常类(CustomException),这样的优势是为了让程序整洁和简化,因为每个try后面都只有一个catch()即可,但这样做会造成一个问题:不能获取程序中原始发生的异常对象。
怎么才能两全奇美呢?解决思路:
1. 使用父类Exception的特定构建器:
public Exception ( string message, Exception innerException ):使用指定错误信息(可以通过Message属性获取)和对作为此异常原因的内部异常的引用来初始化 Exception类的新实例,目的是将程序原发生的异常封装在Exception类对象中。
2. 在捕获异常后,使用Exception的属性提取包含在内部的异常。
public Exception InnerException { get; } :获取导致当前异常的 Exception实例。
范例:
using System;
using System.IO;
public class Test3
{
public static void ReadArticle(string n)
{
if (n == null || n.Trim() == "")
{
FileNotFoundException e = new FileNotFoundException("文件名为空");
e.HelpLink = "error.aspx"; //指定错误的页面
e.Source = "文件读取程序块中(ReadArticle)";
CustomException c = new CustomException("文件读取", e); //包含原始异常类(因由类)
throw c;
}
else
{
//读取文件内容。
}

}
public static void Main(string[] args)
{
try
{
ReadArticle(null);
}
catch (CustomException e)
{//接收抛出的所有类型的异常。
Exception inn = e.InnerException;
Console.WriteLine("错误的信息:"+e.Message);
Console.WriteLine("错误的源头信息:" + e.Source);
Console.WriteLine("错误的堆栈信息:" + e.StackTrace);
Console.WriteLine("错误的指向页面:" + e.HelpLink);
}
}
}
public class CustomException : Exception
{

public CustomException(string n, Exception e):base(n,e)
{

}
}

PS:

1、什么时候该异常处理?
1)代码最外层,如WinFrom,避免用户看到内部异常信息用户体验不好,或者造成程序崩溃,可以用log4net之类的框架记录异常。
2)遇到异常需要恢复状态或者重试的地方。例如连接数据库偶然失败了,可以有个重连机制,在Catch块重新连接数据库。
3)对于一系列有可能失败的任务,其中有一个任务失败,不想影响到其他任务。例如要上传100张图片,不想因为一张图片上传发生异常而失败,进而终止整个上传任务,仅需要记录下失败的图片,提醒用户重传即可。

2、异常处理需要注意的地方
1)Catch和Finally代码应该非常短,而且成功率极高,避免自己又抛出一个异常。否则CLR会终止进程,避免安全漏洞或者不可预知的后果。这个类似于Windows蓝屏,发生了严重的错误,宁愿使系统不可用。
2)Catch块尽量避免直接捕捉异常的基类Exception,而应该捕捉具体的异常类。

3、异常处理的方法和技巧
1)是否能构建统一的框架处理异常,而不用手工来处理呢?
有的人可能会问,能不能偷懒,在一个地方处理异常就行了。如果仅仅是记录异常系统信息,通知到用户,而且这些信息通常是可以缺少一些上下文的,是可以构建同一的机制记录异常信息的。
例如:
WinFrom的Application对象本身就提供了ThreadException时间来捕捉为处理的异常

static void Main()
{
//注册捕捉异常事件
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
Exception ex = e.Exception;
//做一些极其简单的记录异常信息操作
}


又例如:
WebFrom的Global.asax本身就已经定义了void Application_Error(object sender, EventArgs e) 来处理异常

void Application_Error(object sender, EventArgs e)
{
// 在出现未处理的错误时运行的代码
Exception ex = Server.GetLastError();
//处理完异常后清除异常
Server.ClearError();
}


但是很多时候,异常处理,不仅仅只是记录到了错误信息就可以了,有时候是需要失败重试或者清理资源等等,因此,仅仅靠统一构建异常处理框架是不够灵活的,

因此可以一方面统一处理,另外一方面特殊的地方可以另外处理。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: