Fork me on GitHub

异常和状态管理

一、异常处理机制

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保证)

 

posted on 2017-06-24 15:33  *Hunter  阅读(203)  评论(0编辑  收藏  举报

导航

AmazingCounters.com