C# 断言与异常抛出

C# 断言与异常抛出,c#,exception,assertions,C#,Exception,Assertions,我已经阅读了大量关于如何以及何时使用断言的文章(以及StackOverflow上发布的一些其他类似问题),我非常理解它们。但是,我仍然不明白是什么样的动机驱使我使用Debug.Assert,而不是抛出一个简单的异常。我的意思是,在.NET中,对失败断言的默认响应是“停止世界”,并向用户显示一个消息框。虽然这种行为可以修改,但我觉得它非常烦人和多余 要做到这一点,我可以抛出一个合适的异常。这样,我就可以在抛出异常之前轻松地将错误写入应用程序的日志,而且,我的应用程序不一定会冻结 那么,为什么我要使

我已经阅读了大量关于如何以及何时使用断言的文章(以及StackOverflow上发布的一些其他类似问题),我非常理解它们。但是,我仍然不明白是什么样的动机驱使我使用
Debug.Assert
,而不是抛出一个简单的异常。我的意思是,在.NET中,对失败断言的默认响应是“停止世界”,并向用户显示一个消息框。虽然这种行为可以修改,但我觉得它非常烦人和多余 要做到这一点,我可以抛出一个合适的异常。这样,我就可以在抛出异常之前轻松地将错误写入应用程序的日志,而且,我的应用程序不一定会冻结

那么,为什么我要使用
Debug.Assert
而不是普通的异常呢?将断言放在不应该放的地方可能会导致各种“不想要的行为”,因此在我看来,使用断言而不是抛出异常并不会带来任何好处。你同意我的观点,还是我在这里遗漏了什么

注意:我完全理解“理论上”的区别(调试与发布、使用模式等),但在我看来,我最好抛出异常,而不是执行断言。因为如果在生产版本中发现bug,我仍然希望“断言”失败(毕竟,“开销”小得可笑),所以我最好还是抛出一个异常



编辑:在我看来,如果断言失败,这意味着应用程序进入了某种损坏的、意外的状态。那我为什么要继续执行死刑呢?应用程序是在调试版本还是发布版本上运行并不重要。这两种断言都适用于检查程序员对世界的理解。只有当程序员做了错事时,断言才会失败。例如,永远不要使用断言来检查用户输入

断言测试条件为“不可能发生”。例外情况适用于“不应该发生但确实发生”的情况

断言很有用,因为在构建时(甚至运行时),您可以更改它们的行为。例如,通常在发布版本中,甚至不检查断言,因为它们引入了不必要的开销。这也是值得警惕的:您的测试甚至可能无法执行

如果使用异常而不是断言,则会失去一些价值:

  • 代码更加冗长,因为测试和引发异常至少需要两行,而断言只有一行

  • 测试和抛出代码将始终运行,而断言可以编译掉

  • 您与其他开发人员失去了一些沟通,因为断言与检查和抛出的产品代码具有不同的含义。如果您确实在测试编程断言,请使用断言

  • 更多信息:

    编辑: 回应您在帖子中的编辑/注释: 听起来,对于您试图完成的事情类型,使用异常比使用断言更合适。 我认为你遇到的精神障碍是,你正在考虑例外和断言来实现同样的目的,因此你试图找出哪一个是“正确的”使用。虽然在如何使用断言和异常方面可能有一些重叠,但不要混淆它们是同一问题的不同解决方案——它们不是。断言和例外都有自己的目的、优点和缺点

    我本来打算用我自己的话来回答,但这比我想象的更公平:

    assert语句的使用可以是 捕获程序逻辑的有效方法 运行时出现错误,但它们是 容易从生产中过滤掉 代码。一旦开发完成,, 这些冗余数据的运行时成本 可以对编码错误进行测试 通过定义 预处理器符号NDEBUG[哪个 在过程中禁用所有断言] 汇编但是,要确保 还记得放在 断言本身将在 生产版本

    断言最好用于测试 仅当所有 以下暂停:

    * the condition should never be false if the code is correct,
    * the condition is not so trivial so as to obviously be always true, and
    * the condition is in some sense internal to a body of software.
    
    断言几乎不应用于检测出现的情况 在软件正常运行期间。 例如,通常断言应该 不用于检查数据库中的错误 用户的输入。然而,这可能会使 使用断言来验证 调用方已检查用户的 输入


    基本上,对需要在生产应用程序中捕获/处理的事情使用异常,使用断言执行对开发有用但在生产中关闭的逻辑检查

    默认情况下,Debug.Assert将仅在调试版本中工作,因此,如果您想在发布版本中捕获任何类型的不良意外行为,则需要在项目属性中使用异常或启用调试常量(通常认为这不是一个好主意)。

    另一个要点是:

    断言是一个函数或宏 如果是一种假设,它会大声抱怨 不正确。请使用断言来记录 代码中的假设和刷新 出人意料的情况

    “在开发过程中,断言不断涌现 提出相互矛盾的假设,, 意外情况、错误值 传递给例程,等等。”

    他接着补充了一些关于什么应该和不应该被断言的准则

    另一方面,例外情况:

    “使用异常处理来绘制 注意意外情况。 例外情况应以书面形式处理 一种让他们在比赛中变得明显的方式 开发和可回收时 生产代码正在运行。”

    如果你
    // 'public facing' method
    public int DoSomething(List<string> stuff, object doohickey, int limit) {
    
        // validate user input and report problems externally with exceptions
    
        if(stuff == null) throw new ArgumentNullException("stuff");
        if(doohickey == null) throw new ArgumentNullException("doohickey");
        if(limit <= 0) throw new ArgumentOutOfRangeException("limit", limit, "Should be > 0");
    
        return DoSomethingImpl(stuff, doohickey, limit);
    }
    
    // 'developer only' method
    private static int DoSomethingImpl(List<string> stuff, object doohickey, int limit) {
    
        // validate input that should only come from other programming methods
        // which we have control over (e.g. we already validated user input in
        // the calling method above), so anything using this method shouldn't
        // need to report problems externally, and compilation mode can remove
        // this "unnecessary" check from production
    
        Debug.Assert(stuff != null);
        Debug.Assert(doohickey != null);
        Debug.Assert(limit > 0);
    
        /* now do the actual work... */
    }