调试断言与异常

发布于 2024-07-13 06:45:19 字数 1397 浏览 6 评论 0原文

令人惊讶的是,我只能找到一个关于这个主题的先前问题,我只想让社区对我的方法“投信任票”(或不投票!)。

我的看法是:

  • 使用 Debug.Assert 来陈述您期望的事情。 当我们完全控制我们的环境时,例如在一种方法中,将使用此方法 验证一些前置条件和后置条件。
  • 出现异常情况时使用异常。 处理外部资源,即文件、数据库、网络等是理所当然的。 但是......

在下面的场景中它会变得有点模糊。 请注意,这是一个人为的示例,仅供说明之用!

假设我们有类 MyClass,它有一个公共属性 MyMode 和一个方法 GetSomeValueForCurrentMode()。 将 MyClass 视为旨在在库中交付(发布构建)以供其他开发人员使用的类。

我们希望此类的外部用户能够更新 MyMode。 现在,GetSomeValueForCurrentMode() 具有以下逻辑:

switch(MyMode)
{
case Mode.ModeA:
return val1;
case Mode.ModeB:
return val2;
default:
//Uh-uh this should never happen

}

我在这里得到的是 MyClass 的用户已使其处于无效状态。 那么我们应该做什么呢?

默认情况下,我们应该Debug.Assert还是抛出新的InvalidOperationException(或其他)?

有一句口头禅说我们不应该信任我们班级的用户。 如果我们选择 Debug.Assert 并将 MyClass 构建为发布版本(从而删除调试断言),则该类的用户将无法获得有用的信息,表明他们已使其处于无效状态。 但这与另一个口头禅相反,即仅在完全超出您控制的事情发生时才抛出异常。

我发现我在绕圈子——这是那些似乎没有明确的“正确”答案的编程辩论之一。 那么让我们来投票吧!

编辑:我在一个相关的SO问题中注意到了这个回答(设计者使用断言或异常的合同?):

经验法则是,当您尝试捕获自己的错误时应该使用断言,而当尝试捕获其他人的错误时应该使用异常。 换句话说,您应该使用异常来检查公共 API 函数的先决条件,以及每当您获取系统外部的任何数据时。 您应该对系统内部的函数或数据使用断言。

对我来说,这是有道理的,并且可以与下面概述的“断言然后抛出”技术结合起来。

欢迎想法!

Surprisingly I was only able to find one previous question on SO about this subject, and I'd just like to get the community "Vote of Confidence" (or not!) on my approach.

The way I see it is thus:

  • use Debug.Assert to state things you EXPECT would be true. This would be used when we are in complete control over our environment, for example in a method to
    verify some pre and post-conditions.
  • use Exceptions when exceptional circumstances arise. Dealing with external resources, i.e. files, databases, networks etc is a no-brainer. But...

It gets a little murky in the following scenario. Please note that this is a CONTRIVED EXAMPLE for illustration only!

Say we have class MyClass, which has a public property MyMode and a method GetSomeValueForCurrentMode(). Consider MyClass as one intended to be shipped (release built) in a library for use by other developers.

We expect MyMode to be updated by external users of this class. Now, GetSomeValueForCurrentMode() has the following logic:

switch(MyMode)
{
case Mode.ModeA:
return val1;
case Mode.ModeB:
return val2;
default:
//Uh-uh this should never happen

}

What I'm getting at here is that the user of MyClass has left it in an invalid state. So what should we do?

In the default, should we Debug.Assert or throw new InvalidOperationException (or other) ?

There is one mantra that says we should not trust users of our classes. If we choose Debug.Assert and built MyClass as a release build (thereby removing the Debug Asserts) the user of the class wouldn't get helpful information that they had left it in an invalid state. But it's sort of contrary to the other mantra which says only throw exceptions when things completely out of your control happen.

I find I go round in circles with this - one of those programming debates that don't seem to have a definitive 'correct' answer. So let's put it to the vote!

Edit: I noticed this response in a related SO question (Design by contract using assertions or exceptions?):

The rule of thumb is that you should use assertions when you are trying to catch your own errors, and exceptions when trying to catch other people's errors. In other words, you should use exceptions to check the preconditions for the public API functions, and whenever you get any data that are external to your system. You should use asserts for the functions or data that are internal to your system.

To me, this makes sense, and can be coupled with the 'Assert then throw' technique outlined below.

Thoughts welcome!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

九歌凝 2024-07-20 06:45:20

我想说:如果错误存在于其他人的代码中,或者存在于不同子系统的代码中(无论您是否编写),请使用异常。 如果来自外部源(例如文件)的数据存在错误,也可以使用异常。

有时,您不知道数据是否来自外部源。 如果安全性很重要,并且您可能正在处理未经正确验证的外部数据,请使用异常。 如果安全性并不重要,那么我会使用性能作为决定因素:这段代码是否可以在紧密循环中执行? 如果是这样,请考虑使用断言,因为您将在发布版本中获得更好的性能。 否则,使用异常。

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.

浅黛梨妆こ 2024-07-20 06:45:20

这取决于语言,如果你有语法糖,那么你应该使用它。 然而,在 Java 中,需要打开断言才能使其工作,因此异常更好。 然而,有特定的异常总是更好,所以这里应该是 IllegalStateException。

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.

慕巷 2024-07-20 06:45:20

在 .Net 中,当您进行发布构建时,调试类方法不会包含在您的程序中,因此如果此代码进入生产环境,您将不得不抛出异常。

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.

话少情深 2024-07-20 06:45:19

我同意这里大多数人的观点,并遵循合同设计。 您应该尝试清楚地区分已部署代码(合同)中的要求和设计期间的预期状态(调试断言)。

您应该始终将合同断言作为异常抛出(因为它们应该始终是异常的)。 大多数框架都内置了用于捕获调试断言的机制。 但在运行时你应该总是抛出异常。

我使用自定义库来帮助解决此问题(在 C#/VB.NET 中)。 我最近将其发布在 Codeplex (http://www.contractdriven.com/) 上,如果您是对这在实践中如何运作感兴趣。

这样做的一个附带好处是,当您开始更频繁地使用 DbC 时,您很少需要使用调试断言,因为代码中已经写入了显式保证,因此实际上很难进入无效状态。

所以你原来的帖子中的问题...“我在这里得到的是 MyClass 的用户已将其置于无效状态。那么我们应该做什么?”...永远不应该出现。

您可能永远不需要再次调试任何东西! ;-)

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! ;-)

庆幸我还是我 2024-07-20 06:45:19

首先,MyClass 有效当然应该由 MyClass 的不变来表示。

其次,你说“我们期望 MyMode 由此类的外部用户更新” - 当然,此模式的设置器应该具有典型的按合同设计形式(与任何公共函数一样):

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

    // BODY (3)

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

在(5)中你会失败与不变量不成立的尖叫断言违规。 但是在(2)中你会失败,因为传递的模式m无效。这会向用户发送一个明确的消息,从而解决你的问题。

请不要告诉我模式字段是公共的,用户可以在没有任何控制的情况下更改它。

编辑:关于断言和发布模式,另请参阅:

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:

帝王念 2024-07-20 06:45:19

我基本上同意你自己问题的结论:如果 Alice 的代码检测到 Alice 犯的错误,则属于 Assert (并且断言应该保留在生产代码中,除非性能另有要求)。 如果 Alice 的代码检测到 Eve 的代码中存在错误,则属于 Exceptions,假设 Alice 和 Eve 位于错误跟踪软件的对立面

现在这是一般的经验法则。 断言,以稍微修改的形式,也可以用作“开发人员注意!”机制(然后它们不应该被称为“ASSERT”,而是“HEADS_UP”或类似的东西)。 如果您的公司开发客户端/服务器产品,并且服务器向客户端发送无效数据怎么办? 如果您是客户端程序员,您会想将其视为外部数据(即 Eve 的数据,即 kaputt)并且希望抛出异常。 但是“较软”的断言可以使 Visual Studio 的调试器立即停止,这可能是尽早检测这些问题并将其报告给服务器团队的好方法。 在真实的安装中,很可能是 Mallory 对 Eve 和 Alice 之间的数据进行了调整,但大多数时候这实际上是您的一位同事的错误,并且您希望在它发生时看到它 - 这就是为什么我称他们为“注意”断言:它们不会取代异常,但它们会给您警告和检查问题的机会。

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.

随风而去 2024-07-20 06:45:19

通常两者都是:断言,然后抛出。

您断言是因为您想通知开发人员开发过程中的错误假设。

您抛出这个问题是因为如果这种情况发生在发布版本中,您需要确保系统在处于不良状态时不会继续处理。

您的系统所需的可靠性特征可能会影响您的选择,但我认为“断言然后抛出”通常是一个有用的策略。

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.

剩一世无双 2024-07-20 06:45:19

我对断言的使用遵循契约设计的想法。 基本上,您断言传入参数、全局变量和其他状态信息在您进入函数时有效,并断言返回值和状态在您离开时也有效。 如果你在开始时得到一个失败的断言,那么它是调用代码中的一个错误,如果你在最后得到它,那么错误就在这段代码中。 这些是前置条件和后置条件。

仅当您检查了输入的前提条件时, switch 语句中的断言才真正有用。 如果在这种情况下您的函数中间达到不可接受的状态,则表明您的函数或被调用函数出现故障。 就我个人而言,我会坚持在这里断言,因为它不是例外。 正如您所暗示的,异常与资源有关,而不是错误。 但是,您可以创建一个存在于发布版本中的自定义断言处理程序,并在失败时抛出异常,以使您的程序有机会返回到稳定状态。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文