Debug.Assert vs Exceptions

♀尐吖头ヾ 提交于 2019-11-28 18:49:30

I agree with most people here and follow Design-by-Contract. You should try and differentiate very clearly between requirements in deployed code (Contracts) and figuring out expected state during design (Debugging Assertions).

You should ALWAYS throw contract assertions as exceptions (as they should always be exceptional). There are mechanisms built in to most frameworks for catching debug assertions. But at runtime you should always throw an exception.

I use a custom library to help with this (in C#/VB.NET). I recently put up it up on Codeplex (http://www.contractdriven.com/) if you're interested in how this works in practice.

A side benefit of this is that as you start using DbC more regularly, you seldom need to use debugging assertions as there are already explicit guarantees written in to your code, so it's actually difficult to get in to an invalid state.

So the question in your original post... "What I'm getting at here is that the user of MyClass has left it in an invalid state. So what should we do?"...should never arise.

You may never need to debug anything again! ;-)

Daniel Daranas

First, MyClass being valid should be, of course, expressed by MyClass's invariant.

Second, you say "We expect MyMode to be updated by external users of this class" - of course the setter of this mode should have the typical design-by-contract form (as any public function):

  void Setter(mode m)
  {
    // INVARIANT ASSERT (1)
    // PRECONDITION ASSERTS (uses "m") (2)

    // BODY (3)

    // POSTCONDITION ASSERTS (if any) (4)
    // INVARIANT ASSERT (5)
  }

In (5) you would fail with a screaming assertion violation that the invariant doesn't hold. But in (2) you would fail, earlier, because the mode m passed is invalid. This would send a clear message to the user and thus solves your problem.

And please don't tell me that the mode field is public and users change it without any control whatsoever.

Edit: About assertions and release mode, see also:

I basically agree with the conclusion of your own question: if Alice's code detects a mistake Alice made, it's a case for Assert (and assertions should be left on in production code, unless performance dictates otherwise). If Alice's code detects a mistake in Eve's code, it's a case for Exceptions, assuming that Alice and Eve are on opposite sides of your bug-tracking software.

Now that's a general rule of thumb. Assertions, in a slightly modified form, can also be used as a "heads-up, developer!" mechanism (and then they should not be called "ASSERT" but "HEADS_UP" or something similar). What if your company develops a client/server product, and the server sends invalid data to the client? If you're a client programmer, you feel like treating it as external data (it is Eve's data that is kaputt) and you want to throw an exception. But a "softer" assertion, which makes Visual Studio's debugger halt right there, can be a very good way to detect those problems really early and report it to the server team. In a real installation, it could very well be Mallory tempering with the data between Eve and Alice, but most of the time it's really a bug by one of your colleagues, and you want to see it when it happens - that's why I call them "heads-up" assertions: they don't replace exceptions, but they give you a warning and a chance to inspect the problem.

Often both: Assert, then throw.

You assert because you want to notify developers of a mistaken assumption during development.

You throw because if this happens in a release build, you need to make sure the system doesn't continue processing while in a bad state.

The desired reliability characteristics of your system may affect your choice here, but 'assert then throw' is often a useful strategy, I think.

My use of asserts follows ideas from design by contract. Basically, you assert that incoming parameters, globals variables and other state information is valid as you enter your function, and assert the return values and state is also valid as you leave. If you get a failed assert at the start, it is a bug in the calling code, if you get it at the end, the bug is in this code. These are pre-conditions and post conditions.

An assert in your switch statement is only really useful if you have checked your pre-conditions on entry. If you arrive at an unacceptable state in the middle of your function in this scenario, it is a failing in your function or a called function. Personally, I would stick with an assert here as it is not an exception. As you imply, exceptions relate to resources, not bugs. You can however create a custom assertion handler that exists in the release build and throws an exception on failure, to give your program an opportunity to return to a stable state.

I would say: use an exception if the bug is in someone else's code, or in code in a different subsystem (whether you wrote it or not). Also use an exception if there is an error in data that came from an external source such as a file.

Sometimes, you don't know whether the data came from an external source or not. If security is important and there's any possibility that you're dealing with external data that has not been verified correct, use an exception. If security is not important then I would use performance as a tiebreaker: might this code be executed in a tight loop? If so consider using an assert, as you'll get better performance in a Release build. Otherwise, use an exception.

It depends on language, is assert if you syntax sugar then you should use it. however in Java asserts need to be turn on for this to work, so exception is better. However its always better to have specific exception, so here it should be IllegalStateException.

In .Net, Debug class methods are not included in your program when you do make a release build, so you will have to throw an exception if this code makes it to production.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!