博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
异常和状态管理
阅读量:4460 次
发布时间:2019-06-08

本文共 6097 字,大约阅读时间需要 20 分钟。

一、异常处理机制

1,应该在try中放置多少代码?

取决于状态管理。如果在一个try块中执行多个可能抛出同一个异常类型的操作,但不同的操作有不同的异常恢复措施,则应该将每个操作都放到他自己的try块中,这样才能正确地恢复状态
2,try、finally,catch执行顺序

try            {                try                {                    throw new Exception("异常");                }                finally                {                    Console.WriteLine("finally");                }            }            catch             {                Console.WriteLine("catch");            }            //输出顺序:finally catch            Console.ReadLine();

3,finally设计

private void ReadData(string pathname)        {            FileStream fs = null;            try            {                fs = new FileStream(pathname, FileMode.Open);                //处理文件中的数据            }            catch (IOException)            {                //在此添加从IOException恢复的代码            }            finally            {                //确保文件被关闭                if(fs!=null)fs.Close();            }        }

二、System.Exception类

属性名称

访问

类型

说明

Message

只读

String

包含辅助性文字说明,指出抛出异常的原因。

如果抛出的异常未处理,该消息通常被写入日志。

由于最终用户一般不看这种消息,所以消息应提供尽可能多的技术细节,方便开发人员修正代码

Data

只读

IDictionary

引用一个“键/值对”集合。

代码在抛出异常前在该集合中添加记录项;捕捉异常的代码可在异常恢复过程中查询记录项并利用其中的信息

Source

读/写

String

包含生成异常的程序集名称

StackTrace

只读

String

包含抛出异常之前调用过的所有方法的名称和签名,该属性对调试很有用

TargetSite

只读

MethodBase

包含抛出异常的方法

HelpLink

只读

String

包含帮助用户理解异常的一个文档的URL(例如file://c:/myapp/help.html#MyExceptionHelp)。但要注意,健全的编程和安全实践阻止用户查看原始未处理的异常。因此,除非希望将信息传达给其他程序员,否则不要使用该属性

InnerException

只读

Exception

如果当前异常是在处理一个异常时抛出,该属性就指出上一个异常是什么。

这个属性通常为null。

Exception类型还提供了公共方法GetBaseException来遍历由内层构成的链表,并返回最初抛出的异常

HResult

读/写

Int32

托管代码喝本机代码边界时使用的一个32位值。

例如:当COM API返回代表失败的HRESULT值时,CLR抛出一个Exception派生对象,并通过该属性来维护HRESULT值

//禁止JIT编译器在调试喝发布生成时对该方法进行内联处理        [MethodImpl(MethodImplOptions.NoInlining)]        private void aa()        {        }

 

三、设计规范和最佳实践

1,善用finally块

①无论线程抛出什么类型的异常,finally块中的代码都会执行

②应该先用finally块清理那些已成功启动的操作,再返回至调用者执行finally块之后的代码

③需经常利用finally块显式释放对象以避免资源泄露

 

只要使用了lock、using、foreach语句、重写类的析构器时c#编译器也会自动生成try/finally块。使用这些构造时,编译器将你写的代码放到try块内部,并将清理代码放到finally块中

①使用lock语句时,锁在finally块中释放

②使用using语句时,在finally块中调用对象的Dispose方法

③使用foreach'语句时,在finally快中调用IEnumerator对象的Dispose方法

④定义析构方法时,在finally块中调用基类的Finalize方法

2,不要什么都捕捉

3,得体地从异常中恢复

public string CalculateSpreadsheetCell(int row, int column)        {            string result;            try            {                result = /*计算电子表格单元格中的值*/            }            catch (DivideByZeroException) //捕捉被零整除错误            {                result = "can't show value:divide by zero";            }            catch (OverflowException)//捕捉溢出错误            {                result = "can't show value:too big";            }            return result;        }

4,发生不可恢复的异常时回滚部分完成的操作——维持状态

public void SerializeObjectGraph(FileStream fs, IFormatter formatter, object rootObj)        {            //保存文件的当前位置            Int64 beforeSerializetion = fs.Position;//获取或设置此流的当前位置            try            {                //尝试将对象图序列化到文件中                formatter.Serialize(fs, rootObj);            }            catch //捕捉所有异常            {                //任何事情出错,就将文件恢复到一个有效的状态                fs.Position = beforeSerializetion;                //截断文件                fs.SetLength(fs.Position);                //注意:上述代码没有放到finally快中,因为只有在序列化失败时才对流进行重置                //重新抛出相同的异常,让调用者知道发生了什么                throw;            }        }

5,异常实现细节来维系协定

private string m_pathname;//地址簿文件的路径名        public string GetPhoneNumber(string name)        {            string phone;            FileStream fs = null;            try            {                fs = new FileStream(m_pathname, FileMode.Open);                //这里的代码重fs读取内容,直至找到匹配的name                phone = /*已经找到的电话号码*/            }            catch (FileNotFoundException e)            {                //抛出一个不同的异常,将name包含到其中,并将原来的异常设为内部异常                throw new NameNotFoundException(name, e);            }            catch (IOException e)            {                //抛出一个不同的异常,将name包含到其中,并将原来的异常设为内部异常                throw new NameNotFoundException(name, e);            }            finally            {                if(fs!=null)fs.Close();            }            return phone;        }

 通过反射调用方法时,CLR内部捕捉方法抛出的任何异常,并把它转换成一个TargetInvocationException

四、未处理的异常

1,异常抛出时,CLR在调用栈中向上查找与抛出的异常对象类型匹配的catch块。没有任何catch块匹配抛出的异常类型,就发生一个未处理的异常。CLR检测到继承中的任何线程有未处理的异常,都会禁止进程

2,类库开发人员不用去处理未处理的异常,应用程序开发人员需要处理,Micorosoft建议应用程序开发人员接受CLR默认策略(应用程序发生未处理的异常时,windows会想事件日志写入一条记录,通过windows日志->应用程序 查看)

五、约束执行区域(CER)

根据定义,CER是必须对错误有适应力的代码块

using System;using System.Collections.Generic;using System.Linq;using System.Runtime.CompilerServices;using System.Runtime.ConstrainedExecution;using System.Text;using System.Threading.Tasks;namespace ConsoleApplication1{    class Program    {        static void Main(string[] args)        {            //强迫finally块中的代码提前准备好            RuntimeHelpers.PrepareConstrainedRegions();//using System.Runtime.CompilerServices;            try            {                Console.WriteLine("In Try");            }            finally            {                //隐士调用Type1的静态构造器(调用S方法不会先输出.ctor exception)                Type1.M();            }            Console.ReadLine();            //输出:.ctor exception            //In Try        }    }    public sealed class Type1    {        static Type1()        {            //如果这里抛出异常,M就得不到调用            Console.WriteLine(".ctor exception");        }        //using System.Runtime.ConstrainedExecution;        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]        public static void M() { }        public static void S() { }    }}

 

1,PrepareConstrainedRegions是一个很特别的方法。

①JIT编译器如果发现在一个try块之前调用了这个方法,就会提前编译与try关联的catch和finally块中的代码。
②JIT编译器会加载任何程序集,创建任何类型对象,调用任何静态构造器,并对任何方法进行JIT编译
③如果其中任何操作造成异常,这个异常会在线程假如try块之前发生
④JIT编译器提前准备方法时,会遍历整个调用图,提前准备被调用的方法,前提是这些方法应用了ReliabilityContractAttribute(并且传递的是Consistency.WillNotCorruptState或者Consistency.MayCorruptInstance)
⑤如果你写的方法保证不损坏任何状态,就用Consistency.WillNotCorruptState,否则就用其他三个值之一来申明方法可能损坏哪一种状态
⑥如果方法保证不会失败,就用Cer.Success,否则用Cer.MayFail(Cer.None这个值表明方法不就行CER保证)

 

转载于:https://www.cnblogs.com/zd1994/p/7073500.html

你可能感兴趣的文章
[SCOI2005]骑士精神
查看>>
Hibernate原理解析-Hibernate中实体的状态
查看>>
六时车主 App 隐私政策
查看>>
C语言常见问题 如何用Visual Studio编写C语言程序测试
查看>>
Web用户的身份验证及WebApi权限验证流程的设计和实现
查看>>
hdu 2098 分拆素数和
查看>>
ECMAScript6-let与const命令详解
查看>>
iOS 使用系统相机、相册显示中文
查看>>
什么是敏捷设计
查看>>
SCSS的基本操作
查看>>
"安装程序无法定位现有系统分区" 问题解决
查看>>
.NET中栈和堆的比较
查看>>
【莫队】bzoj 3781,bzoj 2038,bzoj 3289
查看>>
如何优化limit
查看>>
几种常用数据库字段类型查询语句
查看>>
字符全排列
查看>>
提高效率必须改掉的7种习惯
查看>>
Java判断语句中判断条件的执行顺序
查看>>
Windows平台下tomcat+java的web程序持续占cpu问题调试
查看>>
OO第四次博客作业!
查看>>