【C#】详解C#异常
目录结构:
在这篇文章中,笔者会阐述C#中的异常。C#是一门面向对象的语言,面向对象编程极大的提高了开发人员的效率。
比如:
Boolean b= "JReE".Substring(1, 1).ToUpper().EndsWith("E");//true
很容易书写这行代码,也很容易维护和阅读。但是这有一个前提,就是不发生错误,但是错误总是存在的。.NET Framework通过异常来处理这个问题。
1.异常处理机制
以下C#代码展示了异常处理的标准用法:
public void SomeMethod() {
try
{
//需要得体的进行恢复/清理的代码放在这里
}
catch (InvalidOperationException e)
{
//从InvalidOperationException恢复的代码放在这里
}
catch (IOException e)
{
//从IOException恢复的代码放在这里
}
catch
{
//除了上述异常的所有异常恢复都放在这里
...
//通常情况下,若什么异常都捕捉,那么需要重新抛出异常
throw;
}
finally {
//对try块中的使用到的资源进行清理
//这里的代码总是执行
}
//如果try块没有抛出异常,或是某个catch块捕捉到异常,但没有抛出,那么执行以下的代码
...
}
1.1 try块
如果代码需要执行一般性的恢复操作,需要进行资源清理,或者两者都需要。那么就应该放到try块中,然后从catch块中恢复,从finally块中清理资源。
C#的编译器不会强制要求进行异常捕获,这一点没有java支持的好。
例如:
static void SomeMethod() {
throw new InvalidCastException("类型转化失败");
}
在这里定义了一个SomeMethod方法,调用者可以直接使用SomeMethod方法,不需要进行捕获,可以正常通过编译。通常情况下,可以为方法添加注释,在Visual Studio开发时候就可以提醒开发人员,该方法抛出那些异常。
/// <summary>
/// 方法测试
/// </summary>
/// <exception cref="System.InvalidCastException">类型转化失败时抛出</exception>
static void SomeMethod() {
throw new InvalidCastException("类型转化失败");
}
如果你没有注释方法的异常信息,那么你代码的调用者就不知道你的代码可能会抛出那些异常,就不能针对性地从异常中恢复。
开发人员若是不知道这些信息,为了增强程序的健壮性,就会使用catch(Exception e)的语言,但这样的语句在开发中是应该避免的。
1.2 catch块
catch块中是响应异常需要执行的代码。一个try块可以关联至少0个catch块。如果try块中的代码抛出执行异常,那么不会执行catch块。catch关键值里面圆括号的表达式被称为捕捉类型,C#要求所以的异常类型必须是System.Exception派生的。
1.3 finally块
finally块中的代码是保证是会执行的。一般在finnaly块中执行try块所需要执行的资源清理操作。
例如:
FileStream file = null;
try
{
file = new FileStream("test.txt", FileMode.Open);
}
catch (IOException e)
{
//执行从IOException恢复的操作
}
finally {
//执行资源清理
if (file != null) {
file.Close();
}
}
2.自定义异常
C#的规范规定System.Exception是所有异常的基类,然后System.ApplicationException和System.SystemException直接从System.Exception派生,另外,System.ApplicationException是所有用户自定义异常的基类,System.SystemException是所有系统异常类的基类。
但这一规定并没有得到很好的执行,比如System.InvalidTimeZoneException直接派生于System.Exception类,像这种"不守规范"的异常类还有很多,所以说System.ApplicationException和System.SystemException类型没有什么特殊的含义了。
自定义异常建议直接从System.Exception派生,设计自己的异常通常是一件比较繁琐的事,因为从System.Exception派生异常都应该是序列化的。
/// <summary>
/// 定义一个泛型异常类,该类主要用于完成序列化操作
/// </summary>
/// <typeparam name="TExceptionArgs"></typeparam>
[Serializable]
public sealed class Exception<TExceptionArgs> : Exception
where TExceptionArgs : ExceptionArgs
{
private const String c_args = "Args";//用作(反)序列化的key
private readonly TExceptionArgs m_args = null; public Exception(String Message = null, Exception InnerException = null):
this(null,Message,InnerException) {
}
public Exception(TExceptionArgs m_args = null, String Message = null, Exception InnerException = null) :
base(Message,InnerException){
this.m_args = m_args;
}
/// <summary>
/// 该构造器用于反序列化,如果该类是密封的,那么该构造器应该是私有的。
/// 如果该类不是密封的,那么该构造器应该是受保护的。
/// </summary>
/// <param name="info"></param>
/// <param name="context"></param>
[SecurityPermissionAttribute(SecurityAction.Demand,SerializationFormatter=true)]
private Exception(SerializationInfo info, StreamingContext context):
base(info,context)
{
m_args = (TExceptionArgs)info.GetValue(c_args, typeof(TExceptionArgs));
}
/// <summary>
/// 该方法用于序列化
/// </summary>
/// <param name="info"></param>
/// <param name="context"></param>
[SecurityPermissionAttribute(SecurityAction.Demand,SerializationFormatter=true)]
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue(c_args,m_args);
base.GetObjectData(info,context);
}
public override string Message {
get {
String baseMsg = base.Message;
return String.IsNullOrEmpty(m_args.Message) ? baseMsg : baseMsg + "("+m_args.Message+")";
}
}
public override Boolean Equals(Object obj)
{
Exception<TExceptionArgs> other = obj as Exception<TExceptionArgs>;
if (other == null) {
return false;
}
return Object.Equals(m_args, other.m_args) && base.Equals(obj);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
Exception<TExceptionArgs>.csc
然后定义约束类ExceptionArgs:
[Serializable]
public abstract class ExceptionArgs{
public virtual String Message {
get {
return String.Empty;
}
}
}
ExceptionArgs.csc
有了上面两个类,然后再来定义其他类型的类就方便了。下面定义一个代表磁盘已满的类,
/// <summary>
/// 代表磁盘已满的异常类
/// </summary>
[Serializable]
public class DiskFullExceptionArgs : ExceptionArgs {
private readonly String m_diskpath; public DiskFullExceptionArgs(String m_diskpath)
{
this.m_diskpath = m_diskpath;
} public override string Message
{
get
{
return m_diskpath == null ? base.Message : "Diskpath="+m_diskpath;
}
}
}
然后可以使用如下的代码来调用:
try {
throw new Exception<DiskFullExceptionArgs>(new DiskFullExceptionArgs(@"c:\"), "disk is full");
}catch(Exception e){
Console.WriteLine(e.Message);
}
3.CLS异常和非CLS异常
在这里首先铺垫一张图片,介绍一下CLS和CLR的关系:
从图中我们可以清楚知道CLR和CLS的关系,CLS的全称是公共语言规范(Common Language Specification)。
所有面向CLR的编程语言都必须支持从Exception派生的异常对象,这是CLS的对此的硬性规定。但是CLR实际上允许抛出任何类型的实例异常,而且有些编程语言运行代码抛出非CLS相容的异常,但C#编译器只允许抛出从Exception派生的异常类型,
在CLR2.0以前,程序员写catch块来捕捉异常时,只能捕捉与CLS相容的异常。如果用C#方法调用了另一种编程语言的代码,然后另一种编程语言的代码抛出一种非CLS相容的异常,那么C#代码更本不能捕捉这个异常。在CLR2.0的版本中,Microsoft引入了一种System.Runtime.CompilerServices.RuntimeWrappedException类,该派生自Exception类,所以该类是一个CLS相容的异常类型。在CLR2.0中,非CLS相容的异常类型抛出的时候,CLR会自动构建一个RuntimeWrappedException实例,并且初始化该类的实例,使其应用实际抛出的对象。
关于CLR的版本,可以在%SystemRoot%\Microsoft.NET目录下查看。
这样就CLR就可以把非CLS异常转化为CLS异常了。现在看一看捕捉非CLS的形式代码:
try
{
//需要得体的进行恢复或清理的代码
}
catch (Exception e)
{
//在CLR2.0以前,这个块只能捕捉与CLS相容的异常
//在CLR2.0及以后,这个块能捕捉与CLS相容和CLS不相容的异常
throw;//重新抛出异常
}
catch {
//在CLR所有版本中,这个块都能捕捉与CLS相容和CLS不相容的异常
throw;//重新抛出异常
}
为了进行验证,笔者使用C++来抛出一个非CLS异常,定义一个文件。
ClassLibrary1.h文件
namespace ClassLibrary1 {
public ref class MyClass
{
public:double division(int a,int b){
if(b==){
throw "Division by zero condition!";//抛出一个非CLS的异常
}
return (a/b);
}
};
}
定义了一个MyClass类,然后类中定义了division方法,若除数为0,那么抛出一个字符串异常“Division by zero condition!”。
接下来编译为ClassLibrary1.dll文件,并且在项目中引入该文件,然后使用如下的代码调用:
MyClass myClass=new MyClass();
try
{
double res = myClass.division(, );
Console.WriteLine(res);
}
catch (Exception e)
{
Console.WriteLine(e.GetType() + "\n");//System.Runtime.InteropServices.SEHException
Console.WriteLine(e.Message + "\n");//外部组件发生异常。
}
【C#】详解C#异常的更多相关文章
- 详解Java异常Throwable、Error、Exception、RuntimeException的区别
在Java中,根据错误性质将运行错误分为两类:错误和异常. 在Java程序的执行过程中,如果出现了异常事件,就会生成一个异常对象.生成的异常对象将传递Java运行时系统,这一异常的产生和提交过程称为抛 ...
- 详解 java 异常
Throwable 可以用来表示任何可以作为异常抛出的类(注意,是类不是接口),分为两种: Error(注意!error也是可以throw的,但是不建议) 和 Exception. 其中 Error ...
- Mysql高手系列 - 第20篇:异常捕获及处理详解(实战经验)
Mysql系列的目标是:通过这个系列从入门到全面掌握一个高级开发所需要的全部技能. 这是Mysql系列第20篇. 环境:mysql5.7.25,cmd命令中进行演示. 代码中被[]包含的表示可选,|符 ...
- ORACLE PL/SQL编程详解
ORACLE PL/SQL编程详解 编程详解 SQL语言只是访问.操作数据库的语言,并不是一种具有流程控制的程序设计语言,而只有程序设计语言才能用于应用软件的开发.PL /SQL是一种高级数据库程序设 ...
- JAVA基础——异常详解
JAVA异常与异常处理详解 一.异常简介 什么是异常? 异常就是有异于常态,和正常情况不一样,有错误出错.在java中,阻止当前方法或作用域的情况,称之为异常. java中异常的体系是怎么样的呢? 1 ...
- java基础(十五)----- Java 最全异常详解 ——Java高级开发必须懂的
本文将详解java中的异常和异常处理机制 异常简介 什么是异常? 程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常. Java异常的分类和类结构图 1.Java中的所 ...
- 异常处理器详解 Java多线程异常处理机制 多线程中篇(四)
在Thread中有异常处理器相关的方法 在ThreadGroup中也有相关的异常处理方法 示例 未检查异常 对于未检查异常,将会直接宕掉,主线程则继续运行,程序会继续运行 在主线程中能不能捕获呢? 我 ...
- Java-异常机制详解以及开发时异常设计的原则要求
Java-异常机制详解以及开发时异常设计的原则要求 http://blog.csdn.net/Jack__Frost/article/details/52760930?locationNum=6
- Java 中的异常和处理详解
Java 中的异常和处理详解 原文出处: 代码钢琴家 简介 程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常.异常发生时,是任程序自生自灭,立刻退出终止,还是输出错误 ...
随机推荐
- springboot项目连接数据库报错
学习SpringBoot也没有多久,今天SpringBoot连接数据库的时候报如下错误: java.sql.SQLException: The server time zone value '�й�� ...
- SpringBatch 错误积累
1.如果nextStep在该JOB中还没有配置,也就是说nextStep还不存在的情况下,就会报错 <end on="EIXT WITH IMBALANCE" /> & ...
- Teamviewer 远程控制时 无法正常操作鼠标点击
其中一种可能: 本机开启了360的64位Intel-VT核晶防护后,用Teamviewer远程到本机,远程电脑无法操作本机的鼠标点击(左右键都不行),查看日志显示拦截了模拟按键.关闭核晶防护就可以正常 ...
- [OpenCV-Python] OpenCV 核心操作 部分 III
部分 III核心操作 OpenCV-Python 中文教程(搬运)目录 9 图像的基础操作 目标 • 获取像素值并修改 • 获取图像的属性(信息) • 图像的 ROI() • 图像通道的拆分及合并几乎 ...
- git根据用户过滤提交记录
使用SourceTree 使用gitk
- spring中整合ssm框架注解版
和xml版差不多,只不过创建对象的方式是由spring自动扫描包名,然后命名空间多一行context代码在application.xml中,然后将每个对象通过注解创建和注入: 直接上代码: 1.use ...
- AFP溢出攻击模块afp/loginext
AFP溢出攻击模块afp/loginext 在苹果Mac OS X 10.3.3及以前版本,AFP服务存在缓存区溢出漏洞CVE-2004-0430.利用该漏洞,用户可以基于LoginExt包执行任 ...
- Python3练习题系列(01)
2018-06-13 题目: 根据用户回答做出相应的判断,完成一个“回答-判断”的小游戏 Python3知识点: if, else, elif 实例代码: print("You enter ...
- [POI2010]GRA-The Minima Game
OJ题号:洛谷3507 思路: 如果选了$k_i$,那么你的对手就可以选上所有$\geq{k_i}$的数.那么他其中获得的分数也一定$\geq{k_i}$. 如果你选了$k_i$以及所有$\geq{k ...
- bzoj 1076 状态压缩最优期望
题意: 你正在玩你最喜欢的电子游戏,并且刚刚进入一个奖励关.在这个奖励关里,系统将依次随 机抛出k次宝物,每次你都可以选择吃或者不吃(必须在抛出下一个宝物之前做出选择,且现在决定不吃的宝物以后也不能再 ...