简体   繁体   English

Debug.Assert与异常抛出

[英]Debug.Assert vs Exception Throwing

I've read plenty of articles (and a couple of other similar questions that were posted on StackOverflow) about how and when to use assertions, and I understood them well. 我已经阅读了很多有关如何以及何时使用断言的文章 (以及在StackOverflow上发布的其他几个类似问题),并且我对它们很了解。 But still, I don't understand what kind of motivation should drive me to use Debug.Assert instead of throwing a plain exception. 但是,我仍然不明白应该采用Debug.Assert动机驱使我使用Debug.Assert而不是抛出一个普通的异常。 What I mean is, in .NET the default response to a failed assertion is to "stop the world" and display a message box to the user. 我的意思是,在.NET中,对失败的断言的默认响应是“停止运行”并向用户显示消息框。 Though this kind of behavior could be modified, I find it highly annoying and redundant to do that, while I could instead, just throw a suitable exception. 尽管可以修改这种行为,但我发现这样做非常烦人和多余,而我可以抛出一个适当的异常。 This way, I could easily write the error to the application's log just before I throw the exception, and plus, my application doesn't necessarily freeze. 这样,在抛出异常之前,我可以轻松地将错误写入应用程序日志,此外,我的应用程序不一定会冻结。

So, why should I, if at all, use Debug.Assert instead of a plain exception? 那么,为什么要使用Debug.Assert而不是普通的异常呢? Placing an assertion where it shouldn't be could just cause all kinds of "unwanted behavior", so in my point of view, I really don't gain anything by using an assertion instead of throwing an exception. 将断言放置在不应该出现的位置可能会导致各种“有害行为”,因此,根据我的观点,我真的没有通过使用断言而不是抛出异常来获得任何好处。 Do you agree with me, or am I missing something here? 您是否同意我的意见,或者我在这里错过了什么?

Note: I fully understand what the difference is "in theory" (Debug vs Release, usage patterns etc.), but as I see it, I would be better off throwing an exception instead of performing an assert. 注意:我完全理解“理论上”的区别(调试与发行,使用模式等),但是据我所知,我最好抛出一个异常而不是执行一个断言。 Since if a bug is discovered on a production release, I still would want the "assertion" to fail (after all, the "overhead" is ridiculously small), so I'll be better off throwing an exception instead. 由于如果在生产版本中发现错误,我仍然希望“断言”失败(毕竟,“开销”很小),所以我最好抛出一个异常。


Edit: The way I see it, if an assert failed, that means that the application entered some kind of corrupted, unexpected state. 编辑:我认为,如果断言失败,则意味着应用程序进入某种损坏的意外状态。 So why would I want to continue execution? 那我为什么要继续执行呢? It doesn't matter if the application runs on a debug or release version. 应用程序是在调试版本还是发行版本上运行都没有关系。 The same goes for both 两者都一样

Though I agree that your reasoning is plausible -- that is, if an assertion is violated unexpectedly, it makes sense to halt execution by throwing -- I personally would not use exceptions in the place of assertions. 尽管我同意您的推理是合理的 -也就是说,如果断言被意外违反,则通过抛出中断执行是有意义的-我个人不会在断言的位置使用异常。 Here's why: 原因如下:

As others have said, assertions should document situations that are impossible , in such a manner that if the allegedly impossible situation comes to pass, the developer is informed. 正如其他人所说,断言应记录 不可能发生的情况 ,以这种方式,如果所谓的不可能发生的情况发生,则通知开发人员。 Exceptions, by contrast, provide a control flow mechanism for exceptional, unlikely, or erroneous situations, but not impossible situations. 相比之下,异常为异常,不太可能或错误的情况提供了控制流机制,但并非并非不可能。 For me, the key difference is this: 对我而言,关键区别在于:

  • It should ALWAYS be possible to produce a test case which exercises a given throw statement. 总是有可能产生一个执行给定throw语句的测试用例。 If it is not possible to produce such a test case then you have a code path in your program which never executes, and it should be removed as dead code. 如果不可能产生这样的测试用例,则您的程序中有一条代码路径永远不会执行,因此应将其作为无效代码删除。

  • It should NEVER be possible to produce a test case which causes an assertion to fire. 绝不可能产生导致断言触发的测试用例。 If an assertion fires, either the code is wrong or the assertion is wrong; 如果断言被触发,则说明代码错误或断言是错误的。 either way, something needs to change in the code. 无论哪种方式,都需要在代码中进行某些更改。

That's why I would not replace an assertion with an exception. 这就是为什么我不会用异常代替断言的原因。 If the assertion cannot actually fire, then replacing it with an exception means you have an untestable code path in your program . 如果断言实际上无法触发,则用异常替换它意味着您的程序中具有不可测试的代码路径 I dislike untestable code paths. 我不喜欢无法测试的代码路径。

Assertions are used to check the programmer's understanding of the world. 断言用于检查程序员对世界的理解。 An assertion should fail only if the programmer has done something wrong. 仅当程序员做错了什么时,断言才应该失败。 For example, never use an assertion to check user input. 例如,切勿使用断言来检查用户输入。

Asserts test for conditions that "cannot happen". 断言测试“不可能发生”的条件。 Exceptions are for conditions that "should not happen but do". 例外情况是“不应发生但会发生”。

Assertions are useful because at build time (or even run time) you can change their behavior. 断言非常有用,因为在构建时(甚至在运行时),您可以更改其行为。 For example, often in release builds, the asserts aren't even checked, because they introduce unneeded overhead. 例如,在发行版本中,断言甚至不会被检查,因为它们会引入不必要的开销。 This is also something to be wary of: your tests may not even be executed. 这也是要注意的一点:您的测试甚至可能不会执行。

If you use exceptions instead of asserts, you lose some value: 如果使用异常而不是断言,则会失去一些价值:

  1. The code is more verbose, since testing and throwing an exception is at least two lines, while an assert is only one. 代码更加冗长,因为测试和引发异常至少需要两行,而断言仅是一行。

  2. Your test and throw code will always run, while asserts can be compiled away. 您的测试和引发代码将始终运行,而断言可以被编译掉。

  3. You lose some communication with other developers, because asserts have a different meaning than product code that checks and throws. 您失去了与其他开发人员的交流,因为断言与检查和抛出的产品代码的含义不同。 If you are really testing a programming assertion, use an assert. 如果您确实在测试编程断言,请使用断言。

More here: http://nedbatchelder.com/text/assert.html 此处更多信息: http : //nedbatchelder.com/text/assert.html

EDIT: In response to the edit/note you made in your post: It sounds like using exceptions are the right thing to use over using assertions for the type of things you are trying to accomplish. 编辑: 响应您在帖子中所做的编辑/注释:听起来,对于要尝试完成的事情类型,使用异常是正确的选择,而不是使用断言。 I think the mental stumbling block you are hitting is that you are considering exceptions and assertions to fulfill the same purpose, and so you are trying to figure out which one would be 'right' to use. 我认为您遇到的心理绊脚石是您正在考虑使用异常和断言来实现相同的目的,因此您正在尝试找出使用哪种“正确”方法。 While there may be some overlap in how assertions and exceptions can be used, don't confuse that for them being different solutions to the same problem- they aren't. 尽管断言和异常的使用方式可能有所重叠,但请不要混淆它们对于同一问题的不同解决方案,而不能混淆。 Assertions and Exceptions each have their own purpose, strengths, and weaknesses. 断言和异常各有其目的,优点和缺点。

I was going to type up an answer in my own words but this does the concept better justice than I would have: 我本来要用自己的话来输入答案,但这确实比我想象的要好得多:

C# Station: Assertions C#站:断言

The use of assert statements can be an effective way to catch program logic errors at runtime, and yet they are easily filtered out of production code. 使用断言语句可以是在运行时捕获程序逻辑错误的有效方法,但是很容易将它们从生产代码中过滤掉。 Once development is complete, the runtime cost of these redundant tests for coding errors can be eliminated simply by defining the preprocessor symbol NDEBUG [which disables all assertions] during compilation. 一旦开发完成,只需在编译期间定义预处理器符号NDEBUG [可禁用所有断言],就可以消除这些冗余测试的代码错误的运行时成本。 Be sure, however, to remember that code placed in the assert itself will be omitted in the production version. 但是,请务必记住,置于生产环境中的代码本身将在生产版本中被省略。

An assertion is best used to test a condition only when all of the following hold: 仅当以下所有条件成立时,才最好使用断言来测试条件:

 * 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. 

Assertions should almost never be used to detect situations that arise during software's normal operation. 断言几乎绝不应用于检测软件正常运行期间出现的情况。 For example, usually assertions should not be used to check for errors in a user's input. 例如,通常不应使用断言来检查用户输入中的错误。 It may, however, make sense to use assertions to verify that a caller has already checked a user's input. 但是,使用断言来验证呼叫者已检查用户输入可能是有意义的。

Basically, use exceptions for things that need to be caught/dealt with in a production application, use assertions to perform logical checks that will be useful for development but turned off in production. 基本上,将异常用于生产应用程序中需要捕获/处理的事物,使用断言执行逻辑检查,这将对开发有用,但在生产中已关闭。

I think a (contrived) practical example may help illuminate the difference: 我认为(人为的)实际示例可能有助于阐明差异:

(adapted from MoreLinq's Batch extension ) (改编自MoreLinq的Batch扩展

// '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... */
}

So as Eric Lippert et al have said, you only assert stuff that you expect to be correct, just in case you (the developer) accidentally used it wrong somewhere else, so you can fix your code. 因此,正如Eric Lippert等人所说的那样,您只能断言您希望正确的内容,以防万一 (开发人员)在其他地方不小心使用了它 ,以便您可以修复代码。 You basically throw exceptions when you have no control over or cannot anticipate what comes in, eg for user input , so that whatever gave it bad data can respond appropriately (eg the user). 当您无法控制或无法预料其中的内容时( 例如,对于用户输入) ,您基本上会引发异常,以便使任何给它的坏数据都可以适当地响应(例如,用户)。

Another nugget from Code Complete : Code Complete的另一个块:

"An assertion is a function or macro that complains loudly if an assumption isn't true. Use assertions to document assumptions made in code and to flush out unexpected conditions. ... “断言是一个函数或宏,如果假设不成立,就会大声抱怨。使用断言来记录代码中的假设并清除意外情况。

"During development, assertions flush out contradictory assumptions, unexpected conditions, bad values passed to routines, and so on." “在开发过程中,断言消除了相互矛盾的假设,意想不到的条件,传递给例程的错误值等等。”

He goes on to add some guidelines on what should and should not be asserted. 他接着就应主张和不应主张的内容添加一些准则。

On the other hand, exceptions: 另一方面,例外:

"Use exception handling to draw attention to unexpected cases. Exceptional cases should be handled in a way that makes them obvious during development and recoverable when production code is running." “使用异常处理来引起人们对意外情况的注意。应对异常情况的方式应使其在开发期间显而易见,并在生产代码运行时可恢复。”

If you don't have this book you should buy it. 如果您没有这本书,则应该购买。

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

Use assertions for things which ARE possible but should not happen (if it were impossible, why would you put an assertion?). 使用断言的事情可能的,但不应该发生(如果它是不可能的,为什么你会放一个说法对吗?)。

Doesn't that sound like a case to use an Exception ? 听起来不是使用Exception的案例吗? Why would you use an assertion instead of an Exception ? 为什么要使用断言而不是Exception

Because there should be code that gets called before your assertion that would stop the assertion's parameter being false. 因为应该在断言之前调用某些代码,这将阻止断言的参数为false。

Usually there is no code before your Exception that guarantees that it won't be thrown. 通常,在您的Exception之前没有任何代码可以保证不会引发该代码。

Why is it good that Debug.Assert() is compiled away in prod? 为什么将Debug.Assert()在prod中编译好? If you want to know about it in debug, wouldn't you want to know about it in prod? 如果您想在调试中了解它,您是否不想在产品中了解它?

You want it only during development, because once you find Debug.Assert(false) situations, you then write code to guarantee that Debug.Assert(false) doesn't happen again. 您只需要在开发过程中就可以使用它,因为一旦找到Debug.Assert(false)情况,您就可以编写代码以确保Debug.Assert(false)不再发生。 Once development is done, assuming you've found the Debug.Assert(false) situations and fixed them, the Debug.Assert() can be safely compiled away as they are now redundant. 一旦开发完成,假设您已经找到Debug.Assert(false)情况并进行了修复,则Debug.Assert()可以安全地编译掉,因为它们现在是多余的。

Suppose you are a member of a fairly large team and there are several people all working on the same general code base, including overlapping on classes. 假设您是一个相当大的团队的成员,并且有几个人都在同一通用代码库上工作,包括在类上重叠。 You may create a method that is called by several other methods, and to avoid lock contention you do not add a separate lock to it, but rather "assume" it was previously locked by the calling method with a specific lock. 您可以创建一个由其他几种方法调用的方法,并且为了避免锁争用,您无需向其添加单独的锁,而是“假定”该方法先前已由具有特定锁的调用方法锁定。 Such as, Debug.Assert(RepositoryLock.IsReadLockHeld || RepositoryLock.IsWriteLockHeld); 例如,Debug.Assert(RepositoryLock.IsReadLockHeld || RepositoryLock.IsWriteLockHeld); The other developers might overlook a comment that says the calling method must use the lock, but they cannot ignore this. 其他开发人员可能会忽略注释,即调用方法必须使用锁,但他们不能忽略此注释。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM