• 【转】编写高质量代码改善C#程序的157个建议——建议58:用抛出异常代替返回错误代码


    建议58:用抛出异常代替返回错误代码

    CLR异常机制的优点:

    • 正常控制流会被立即中止,无效值或状态不会在系统中继续传播。
    • 提供了统一的处理错误的方法。
    • 提供了在构造函数、操作符重载及属性中报告异常的遍历机制。
    • 提供了异常堆栈,便于开发者定位异常发生的位置。

    不应该将异常机制用于正常控制流中,异常的发生是一个小概率事件,所以异常带来的效率问题会被限制在一个很小的范围内。异常机制带来的效率问题不足以抵消它带来的巨大收益。try catch所带来的效率问题几乎是可以忽略的。

    在异常机制出现之前,应用程序普遍采用错误代码的方式来通知调用之发生了异常。为什么要用抛出异常代替返回错误代码呢?

    对于一个成员方法而言,它要么执行成功,要么执行失败。成员方法执行成功的情况很容易理解,但如果执行失败了却没有那么简单,因为我们需要将执行失败的原因通知调用者。抛出异常和返回错误代码都是用来通知调用者的手段。

    假如我们要实现一个点单的功能:应用程序要完成一次保存新建用户的操作。这是一个分布式的操作,保存动作除了需要将用户保存在本地外,还需要通过WCF在远程服务器上保存数据。负责保存用户的成员如下:

            private static int SaveUser1(User user)
            {
                if (!SaveToFile(user))
                {
                    return 1;
                }
                if (!SaveToDataBase(user))
                {
                    return 2;
                }
                return 0;
            }

    代码看似没有问题的,调用者只要接收到1或2,就知道是哪里出现了问题,但如果执行失败,似乎还可以挖掘出更多的原因:

    在SaveToFile方法中,我们可能会遇到:

    • 程序无数据存储文件写权限导致的失败。
    • 硬盘空间不足导致的失败。

    在SaveToDataBase方法中,可能会遇到:

    • 服务不存在导致的失败。
    • 网络连接不正常导致的失败。

    当我们想要告示调用者更多细节的时候,就需要与调用者约定更多的错误代码。于是。错误代码飞速膨胀,直到看起来似乎无法维护,因为我们总是查找并确认错误代码。

    采用接下来的方法,可能会省略很大一部分的错误代码:

            private static bool SaveUser2(User user,ref string errorMsg)
            {
                if (!SaveToFile(user))
                {
                    errorMsg = "本地保存失败!";
                    return false;
                }
                if (!SaveToDataBase(user))
                {
                    errorMsg = "远程保存失败!";
                    return false;
                }
                return true;
            }

    在没有异常处理前,我们只能返回错误代码。但是,现在有了另一种选择,既使用异常机制。如下:

            private static void SaveUser(User user)
            {
                SaveToFile(user);
                SaveToDataBase(user);
            }
            
            static void Main(string[] args)
            {
                User user = new User();
                try
                {
                    SaveUser(user);
                }
                catch (IOException)
                {
                    //IO异常,通知当前用户
                }
                catch (UnauthorizedAccessException)
                {
                    //权限失败,通知客户端管理员
                }
                catch (CommunicationException)
                {
                    //网络异常,通知发送e-mail给网络管理员。
                }
    
            }        

    使用了CLR异常机制后,代码更加清晰,易于理解。至于效率问题,throw exception产生的那点儿效率损失与等待网络连接异常相比,简直微不足道,而CLR异常机制带来的好处却是显而易见的。

    catch (CommunicationException)代码块中,代码所完成的功能是“通知发送”,而不是“发送”本身,因为我们需要确保在catch和finally中所执行的代码是可以被执行的。尽量不要在catch和finally中再让代码“出错”,那会让异常堆栈信息变得复杂和难以理解。

    在本例中的catch代码块中,不是要真的编写发送邮件的代码,因为发送邮件的这个行为可能会产生更多的异常,而“通知发送”这个行为稳定性更高(即“不出错”)。

    在某些情况下,错误代码将无用武之地,如构造函数、操作符重载及属性。语法的特性决定了其不具备任何返回值,于是异常处理被当做取代错误代码的首要选择。

    转自:《编写高质量代码改善C#程序的157个建议》陆敏技

  • 相关阅读:
    获取账号所有联系人
    获取用户的初始信息展示
    pip升级的错误
    二维码长轮询获取登陆并获取用户基本信息
    获取微信二维码
    WebChat理清流程
    python的requests模块
    python的单例模式和__new__方法
    matplotlib 基础知识汇总
    pandas数据分析案例:美国2012年总统候选人政治献金数据分析
  • 原文地址:https://www.cnblogs.com/farmer-y/p/7992548.html
Copyright © 2020-2023  润新知