.net try catch 异常捕获的正确使用姿势。。

很惭愧,写了好多年的代码, 最基本的try catch 才刚刚会正确的使用,
以前只能说叫会用, 但是用法不正确。
先说说,异常的3种使用方式。 见下面的代码。

        public static int Method1()
        {
            try
            {
                int a=int.Prease("aaaa");
                return Method1_1();
            }
            catch (Exception ex)
            {
                throw;   //方式1, 会丢失本try 代码段内的StackTrace信息
                throw ex;   //方式2  会丢失本try 代码段内的StackTrace信息,包括子函数内的
                throw new Exception(ex); //方式3  新的异常。 会丢失更多。
            }
        }

StackTrace信息 非常重要,能够帮助我们快速定位到异常所在的代码行,帮我们快速的诊断问题代码。 在生产环境中的时候,一般是无法直接调试的。 那么捕获异常并记录异常所在的行,(前提是要提供调试库 pdb文件,生成的时候回一起自动生成。一起复制过去就可以)能够帮助我们非常快速的定位异常做出判断。。

要想能够正确捕获异常信息中的StackTrace信息,根据上面的代码。3中捕获异常并再次抛出的方式都会丢失一部分StackTrace 信息。丢失最小的要数 方式1, 但是,还是丢失了,try 代码块内的StackTrace 信息, 不信你可以自己测试一下。

那么这里就有问题了。 前后逻辑似乎是冲突的嘛。
1.不能调试。
2.不能丢失StackTrace信息
3.捕获了信息要写入日志
4.有些代码必须在出现异常的时候执行处理代码,然后还得再抛出异常。例如数据库事物,在出错的时候要把事物回滚。

当然,我们有方法处理这种问题。 那就是在每一层,写一个日志。
例如

   public static int Method1()
        {
            try
            {
                int a=int.Prease("aaaa");
                return Method1_1();
            }
            catch (Exception ex)
            {
               log.Error(ex); //这里立即写入日志。不会丢失StackTrace信息
               throw ex; //然后直接抛出,后处理。
            }
        }

当然这么干,不是不可以。 但是实际上这样干最大的麻烦是,log对象在每一层都需要, 在每个对象里面都需要。 log成了标配。。 log日志成了头疼的问题。

我记得以前我曾经写过关于异常信息的捕获和处理。 我的建议是
在最外层进行捕获,并写入日志。 中间层和底层不要捕获。
我至今也建议这样写。
这样写,最大的好处是省事。而且也照样能控制异常。

那么问题来了。 中间层, 必须要处理的时候怎么弄呢? 例如涉及到数据库关闭, 文件关闭等。 必须要在出异常关闭资源的时候, 代码怎么写呢?
中间层,写了try catch ,不管用方式1,方式2,方式3,都会丢失 StackTrace信息。
不写又不行。。。

中间层的最终建议。


 public static int DBSave()
        {
            DbContext DB =  DBContextHelper.GetLISDbContext();   
            var tran = DB.Database.BeginTransaction();
            var isCommit = false;
            try
            {
                。。。。中间业务逻辑代码
                tran.Commit();
                isCommit = true;
                return true;
            }
             //catch (Exception ex) 不要捕获异常,直接外层捕获,
            finally
            {
                if (isCommit == false)
                {   //如果未能成功提交,也就是中间发生过异常,那么回滚.这样捕获的好处是, 异常信息的StackStace 不会丢,能快速定位到问题代码行
                    tran.Rollback(); 
                } 
                tran.Dispose();
                DB.Dispose();
            }

        }

最外层,的代码

try{
    ...调用中间层代码。
}
 catch (LisException hisex)
{ //调用对方系统异常.具体信息放在Message中
    res.Code = "600";
    res.Message = hisex.Message;
    res.ExceptionDetails = hisex.ToString();
    MyLog(CallMethodName, ParameterXml, string.Empty, res);
}
catch (Exception ex)
{   // 系统异常.具体信息放在Message中 
    while (ex.InnerException != null) ex = ex.InnerException; //底层的异常容易排除错误
    res.Code = "500";
    res.Message = ex.Message;
    res.ExceptionDetails = ex.ToString();
    //return ex.ToString();
    MyLog(CallMethodName, ParameterXml, string.Empty,  res);
}

对于最顶层, 一般是每个按钮都有一个
try catch
最顶层,配好log就可以和方便的进行日志记录了。不必在每一层都写try catch

你可能感兴趣的:(软件质量,net,架构设计)