编写高质量代码改善C#程序的157个建议[避免finaly内的无效代码、避免嵌套异常、避免吃掉异常、注意循环异常处理]

前言  html

  本文已同步到http://www.cnblogs.com/aehyok/p/3624579.html。本文主要来学习如下几点建议数据库

  建议6一、避免在finally内撰写无效代码less

  建议6二、避免嵌套异常分布式

  建议6三、避免“吃掉”异常ide

  建议6四、为循环增长Tester-Doer模式而不是将try-catch置于循环内性能

建议6一、避免在finally内撰写无效代码学习

先直接来看一下三个简单的try catch方法测试

    public class User
    {
        public string Name { get; set; }
    }

    class Program
    {

        static void Main(string[] args)
        {
            Console.WriteLine(Test1());
            Console.WriteLine(Test2());
            Console.WriteLine(Test3().Name);

            Console.ReadLine();
        }

        public static int Test1()
        {
            int i = 0;
            try
            {
                i = 1;
            }
            finally
            {
                i = 2;
                Console.WriteLine("\t 将int结果改成2,finnally执行完毕。");
            }
            return i;
        }

        public static int Test2()
        {
            int i = 0;
            try
            {
                return i = 1;
            }
            finally
            {
                i = 2;
                Console.WriteLine("\t 将int结果改成2,finnally执行完毕。");
            }
        }

看完代码你内心大概也有了一个答案了吧spa

这些若是经过IL来解释,仍是比较容易的,在此就不进行赘述了。线程

  在CLR中,方法的参数以及返回值都是用栈来保存的。在方法内部,会首先将参数依次压栈,当须要使用这些参数的时候,方法会直接去栈里取用参数值,方法返回时,会将返回值压入栈顶。若是参数的类型是值类型,压栈的就是复制的值,若是是引用类型,则在方法内对于参数的修改也会带到方法外。

建议6二、避免嵌套异常  

在建议59中已经强调过,应该容许异常在调用堆栈中往上传播,不要过多使用catch,而后再throw。果断使用catch会带来两个问题:

一、代码更多了。这看上去好像你根本不知道该怎么处理异常,因此你总在不停地catch.

二、隐藏了堆栈信息,使你不知道真正发生异常的地方。

来看一下下面的代码

        static void Main(string[] args)
        {
            try
            {
                Console.WriteLine("NoTry\n");
                MethodNoTry();
            }
            catch (Exception exception)
            {
                Console.WriteLine(exception.StackTrace);
            }
            try
            {
                Console.WriteLine("WithTry\n");
                MethodWithTry();
            }
            catch (Exception exception)
            {
                Console.WriteLine(exception.StackTrace);
            }
            Console.ReadLine();
        }

        public static int Method()
        {
            int i = 0;
            return 10 / i;
        }

        public static void MethodNoTry()
        {
            Method();
        }

        public static void MethodWithTry()
        {
            try
            {
                Method();
            }
            catch (Exception exception)
            {
                throw exception;
            }
        }

执行结果

  能够发现,MethodNoTry的方法能够查看到发生异常错误的地方,而MethodWithTry根本不清楚发生错误的地方了。调用的堆栈倍重置了。若是这个方法还存在另外的异常,在UI层将永远不知道真正发生错误的地方,给开发者带来不小的麻烦。 

除了在建议59中提到的须要包装异常的状况外,无端地嵌套异常是咱们要极力避免的。固然,若是真得须要捕获这个异常来恢复一些状态,而后从新抛出,代码来起来应该能够这样:

            try
            {
                MethodWithTry();
            }
            catch(Exception)
            {
                ///工做代码
                throw;
            }

或者稍做改动

            try
            {
                MethodWithTry();
            }
            catch
            {
                ///工做代码
                throw;
            }

尽可能避免下面这样引起异常:

            try
            {
                MethodWithTry();
            }
            catch(Exception exception)
            {
                ///工做代码
                throw exception;
            }

直接throw exception而不是throw将会重置堆栈消息。

 

建议6三、避免“吃掉”异常 

  看了建议62以后,你可能已经明白,嵌套异常是很危险的行为,一不当心就会将异常堆栈信息,也就是真正的Bug出处隐藏起来。但这还不是最严重的行为,最严重的就是“吃掉”异常,即捕获而后不向上层throw抛出。

  避免“吃掉”异常,并非说不该该“吃掉”异常,而是这里面有个重要原则:该异常可悲预见,而且一般状况它不能算是一个Bug。

  想象你正在对上万份文件进行解密,这些文件来自不一样的客户端,颇有可能存在文件倍破坏的现象,你的目标就是要讲解密出来的数据插入数据库。这个时候,你不得不忽略那些解密失败的问题,让这个过程进行下去。固然,记录日志是必要的, 由于后期你可能会倍解密失败的文件作统一的处理。

  另一种状况,可能连记录日志都不须要。在对上千个受控端进行控制的分布式系统中,控制端须要发送心跳数据来判断受控端的在线状况。一般的作法是维护一个信号量,若是在一个可接受的阻滞时间如(如500ms)心跳数据发送失败,那么控制端线程将不会收到信号,便可以判断受控端的断线状态。在这种状况下,对每次SocketException进行记录,一般也是没有意义的。

  本建议的所有要素是:若是你不知道如何处理某个异常,那么千万不要“吃掉”异常,若是你一不小“吃掉”了一个本该网上传递的异常,那么,这里可能诞生一个BUg,并且,解决它会很费周折。 

建议6四、为循环增长Tester-Doer模式而不是将try-catch置于循环内

  若是须要在循环中引起异常,你须要特别注意,由于抛出异常是一个至关影响性能的过程。应该尽可能在循环当中对异常发生的一些条件进行判断,而后根据条件进行处理。能够作一个测试:

        static void Main(string[] args)
        {
            CodeTimer.Initialize();

            CodeTimer.Time("try..catch..", 1, P1);

            CodeTimer.Time("Tester-Doer", 1, P2);

            Console.ReadLine();
        }

        public static void P1()
        {
            int x = 0;
            for (int i = 0; i < 1000; i++)
            {
                try
                {
                    int j = i / x;
                }
                catch
                { 
                    
                }
            }
        }

差距至关明显。以上代码中,咱们预见了代码可能会发生DivideByZeroException异常,因而,调整策略,对异常发生的条件进行了特殊处理:Continue,让效率获得了极大的提高。

 

相关文章
相关标签/搜索