Debug.Assert 与特定抛出的异常

发布于 2024-07-04 10:07:15 字数 1044 浏览 7 评论 0原文

我刚刚开始浏览 John Robbins 的《调试 MS .Net 2.0 应用程序》,并且对他对 Debug.Assert(...) 的宣讲感到困惑。

他指出,良好实现的断言在某种程度上存储了错误条件的状态,例如:

Debug.Assert(i > 3, "i > 3", "This means I got a bad parameter");

现在,就我个人而言,他如此喜欢在没有实际合理的“业务逻辑”注释的情况下重述他的测试,这对我来说似乎很疯狂,也许“我由于 flobittyjam widgitification 过程,<= 3 绝不能发生”。

所以,我认为我将断言视为一种低级的“让我们保护我的假设”之类的东西......假设人们觉得这是一个只需要在调试中进行的测试 - 即你正在保护自己免受同事的侵害和未来的程序员,并希望他们能够真正进行测试。

但我不明白的是,他接着说,除了正常的错误处理之外,你还应该使用断言; 现在我的设想是这样的:

Debug.Assert(i > 3, "i must be greater than 3 because of the flibbity widgit status");
if (i <= 3)
{
    throw new ArgumentOutOfRangeException("i", "i must be > 3 because... i=" + i.ToString());
}

通过错误条件测试的 Debug.Assert 重复我得到了什么? 我想如果我们谈论的是一个非常重要的计算的仅调试双重检查,我会得到它......

double interestAmount = loan.GetInterest();
Debug.Assert(debugInterestDoubleCheck(loan) == interestAmount, "Mismatch on interest calc");

但是我没有得到它用于肯定值得检查的参数测试(在调试和发布中)构建)...或不。 我缺少什么?

I've just started skimming 'Debugging MS .Net 2.0 Applications' by John Robbins, and have become confused by his evangelism for Debug.Assert(...).

He points out that well-implemented Asserts store the state, somewhat, of an error condition, e.g.:

Debug.Assert(i > 3, "i > 3", "This means I got a bad parameter");

Now, personally, it seems crazy to me that he so loves restating his test without an actual sensible 'business logic' comment, perhaps "i <= 3 must never happen because of the flobittyjam widgitification process".

So, I think I get Asserts as a kind-of low-level "Let's protect my assumptions" kind of thing... assuming that one feels this is a test one only needs to do in debug - i.e. you are protecting yourself against colleague and future programmers, and hoping that they actually test things.

But what I don't get is, he then goes on to say that you should use assertions in addition to normal error handling; now what I envisage is something like this:

Debug.Assert(i > 3, "i must be greater than 3 because of the flibbity widgit status");
if (i <= 3)
{
    throw new ArgumentOutOfRangeException("i", "i must be > 3 because... i=" + i.ToString());
}

What have I gained by the Debug.Assert repetition of the error condition test? I think I'd get it if we were talking about debug-only double-checking of a very important calculation...

double interestAmount = loan.GetInterest();
Debug.Assert(debugInterestDoubleCheck(loan) == interestAmount, "Mismatch on interest calc");

...but I don't get it for parameter tests which are surely worth checking (in both DEBUG and Release builds)... or not. What am I missing?

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

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

发布评论

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

评论(8

左耳近心 2024-07-11 10:07:15

断言与异常抛出之间存在通信方面的问题。

假设我们有一个带有 Name 属性和 ToString 方法的 User 类。

如果 ToString 是这样实现的:

public string ToString()
{
     Debug.Assert(Name != null);
     return Name;
}

它表示 Name 永远不应该为 null,如果是,则 User 类中存在错误。

如果 ToString 是这样实现的:

public string ToString()
{
     if ( Name == null )
     {
          throw new InvalidOperationException("Name is null");
     }

     return Name;
}

如果 Name 为 null,则表示调用者错误地使用了 ToString,并且应该在调用之前进行检查。

两者的实现都

public string ToString()
{
     Debug.Assert(Name != null);
     if ( Name == null )
     {
          throw new InvalidOperationException("Name is null");
     }

     return Name;
}

表明,如果 Name 为 null,则 User 类中存在错误,但我们无论如何都想处理它。 (用户在拨打电话之前不需要检查姓名。)我认为这就是罗宾斯推荐的那种安全措施。

There is a communication aspect to asserts vs exception throwing.

Let's say we have a User class with a Name property and a ToString method.

If ToString is implemented like this:

public string ToString()
{
     Debug.Assert(Name != null);
     return Name;
}

It says that Name should never null and there is a bug in the User class if it is.

If ToString is implement like this:

public string ToString()
{
     if ( Name == null )
     {
          throw new InvalidOperationException("Name is null");
     }

     return Name;
}

It says that the caller is using ToString incorrectly if Name is null and should check that before calling.

The implementation with both

public string ToString()
{
     Debug.Assert(Name != null);
     if ( Name == null )
     {
          throw new InvalidOperationException("Name is null");
     }

     return Name;
}

says that if Name is null there bug in the User class, but we want to handle it anyway. (The user doesn't need to check Name before calling.) I think this is the kind of safety Robbins was recommending.

一向肩并 2024-07-11 10:07:15

当涉及到提供关于测试问题的调试与断言的指导时,我已经思考了很长时间。

您应该能够使用错误的输入、错误的状态、无效的操作顺序和任何其他可能的错误条件来测试您的类,并且断言应该永远跳闸。 每个断言都会检查某些内容应该始终为真,无论执行的输入或计算如何。

我得出的良好经验法则是:

  1. 断言不能替代独立于配置而正确运行的健壮代码。 它们是互补的。

  2. 在单元测试运行期间决不应该触发断言,即使在输入无效值或测试错误条件时也是如此。 代码应该在不发生断言的情况下处理这些情况。

  3. 如果断言失败(无论是在单元测试中还是在测试期间),则该类被窃听。

对于所有其他错误——通常是由于环境(网络连接丢失)或误用(调用者传递了空值)——使用硬检查和错误检查会更好、更容易理解。 例外情况。 如果发生异常,调用者知道这可能是他们的错。 如果发生断言,调用者就知道这可能是断言所在代码中的错误。

关于重复:我同意。 我不明白为什么您要使用 Debug.Assert 和异常检查来复制验证。 它不仅给代码增加了一些噪音,让谁有错的问题变得更加混乱,而且是一种重复。

I've thought about this long and hard when it comes to providing guidance on debug vs. assert with respect to testing concerns.

You should be able to test your class with erroneous input, bad state, invalid order of operations and any other conceivable error condition and an assert should never trip. Each assert is checking something should always be true regardless of the inputs or computations performed.

Good rules of thumb I've arrived at:

  1. Asserts are not a replacement for robust code that functions correctly independent of configuration. They are complementary.

  2. Asserts should never be tripped during a unit test run, even when feeding in invalid values or testing error conditions. The code should handle these conditions without an assert occurring.

  3. If an assert trips (either in a unit test or during testing), the class is bugged.

For all other errors -- typically down to environment (network connection lost) or misuse (caller passed a null value) -- it's much nicer and more understandable to use hard checks & exceptions. If an exception occurs, the caller knows it's likely their fault. If an assert occurs, the caller knows it's likely a bug in the code where the assert is located.

Regarding duplication: I agree. I don't see why you would replicate the validation with a Debug.Assert AND an exception check. Not only does it add some noise to the code and muddy the waters regarding who is at fault, but it a form of repetition.

烙印 2024-07-11 10:07:15

良好使用 Assert 的示例:

Debug.Assert(flibbles.count() < 1000000, "too many flibbles"); // indicate something is awry
log.warning("flibble count reached " + flibbles.count()); // log in production as early warning

我个人认为,当您知道某些内容超出了所需限制时,才应该使用 Assert,但您可以确定继续是相当安全的。 在所有其他情况下(请随意指出我没有想到的情况)使用异常来快速失败。

对我来说,关键的权衡是您是否想要通过异常来关闭实时/生产系统以避免损坏并使故障排除更容易,或者您是否遇到了一种情况,这种情况永远不应该允许在测试/调试版本中继续被忽视,但可以允许继续生产(当然会记录警告)。

比照。 http://c2.com/cgi/wiki?FailFast
从java问题复制并修改:异常与断言

Example of a good use of Assert:

Debug.Assert(flibbles.count() < 1000000, "too many flibbles"); // indicate something is awry
log.warning("flibble count reached " + flibbles.count()); // log in production as early warning

I personally think that Assert should only be used when you know something is outside desirable limits, but you can be sure it's reasonably safe to continue. In all other circumstances (feel free point out circumstances I haven't thought of) use exceptions to fail hard and fast.

The key tradeoff for me is whether you want to bring down a live/production system with an Exception to avoid corruption and make troubleshooting easier, or whether you have encountered a situation that should never be allowed to continue unnoticed in test/debug versions but could be allowed to continue in production (logging a warning of course).

cf. http://c2.com/cgi/wiki?FailFast
copied and modified from java question: Exception Vs Assertion

沦落红尘 2024-07-11 10:07:15

IMO 这只是开发时间的损失。 正确实施的异常可以让您清楚地了解发生的情况。 我看到太多应用程序显示模糊的“断言失败:i < 10”错误。 我认为断言是一个临时解决方案。 在我看来,程序的最终版本中不应包含任何断言。 在我的实践中,我使用断言进行快速和肮脏的检查。 代码的最终版本应考虑错误情况并做出相应的行为。 如果发生不好的事情,你有两个选择:处理它或离开它。 如果传入错误的参数,函数应该抛出带有有意义描述的异常。我认为验证逻辑没有重复。

IMO it's a loss of development time only. Properly implemented exception gives you a clear picture of what happened. I saw too much applications showing obscure "Assertion failed: i < 10" errors. I see assertion as a temporary solution. In my opinion no assertions should be in a final version of a program. In my practice I used assertions for quick and dirty checks. Final version of the code should take erroneous situation into account and behave accordingly. If something bad happens you have 2 choices: handle it or leave it. Function should throw an exception with meaningful description if wrong parameters passed in. I see no points in duplication of validation logic.

她如夕阳 2024-07-11 10:07:15

断言不用于参数检查。 应始终进行参数检查(并且精确地根据文档和/或规范中指定的前提条件),并根据需要抛出 ArgumentOutOfRangeException

断言用于测试“不可能”的情况,即您(在程序逻辑中)假设的事情是正确的。 这些断言是为了告诉您这些假设是否因任何原因而被打破。

希望这可以帮助!

Assertions are not for parameter checking. Parameter checking should always be done (and precisely according to what pre-conditions are specified in your documentation and/or specification), and the ArgumentOutOfRangeException thrown as necessary.

Assertions are for testing for "impossible" situations, i.e., things that you (in your program logic) assume are true. The assertions are there to tell you if these assumptions are broken for any reason.

Hope this helps!

好多鱼好多余 2024-07-11 10:07:15

这里是 2 美分。

我认为最好的方法是同时使用断言和异常。 恕我直言,这两种方法之间的主要区别是,如果断言语句可以轻松地从应用程序文本中删除(定义、条件属性...),而抛出的异常则依赖于(通常)条件代码,而条件代码更难删除(带有预处理器条件的多个部分)。

每个应用程序异常都应得到正确处理,而断言仅应在算法开发和测试期间得到满足。

如果将空对象引用作为例程参数传递,并且使用该值,则会出现空指针异常。 事实上:为什么你应该写一个断言? 在这种情况下这是浪费时间。
但是类例程中使用的私有类成员又如何呢? 当这些值在某处设置时,最好使用断言检查是否设置了空值。 那只是因为当您使用该成员时,您会遇到空指针异常,但您不知道该值是如何设置的。 这会导致程序重新启动,破坏所有用于设置私有成员的入口点。

异常更有用,但管理它们(恕我直言)可能非常繁重,并且有可能使用太多异常。 而且它们需要额外的检查,可能不希望优化代码。
就我个人而言,我仅在代码需要深度 catch 控制(catch 语句在调用堆栈中非常低)或函数参数未硬编码在代码中时才使用异常。

Here is by 2 cents.

I think that the best way is to use both assertions and exceptions. The main differences between the two methods, imho, if that Assert statements can be removed easily from the application text (defines, conditional attributes...), while Exception thrown are dependent (tipically) by a conditional code which is harder to remove (multine section with preprocessor conditionals).

Every application exception shall be handled correctly, while assertions shall be satisfied only during the algorithm developement and testing.

If you pass an null object reference as routine parameter, and you use this value, you get a null pointer exception. Indeed: why you should write an assertion? It's a waste of time in this case.
But what about private class members used in class routines? When these value are set somewhere, is better to check with an assertion if a null value is set. That's only because when you use the member, you get a null pointer exception but you don't know how the value was set. This cause a restart of the program breaking on all entry point use to set the private member.

Exception are more usefull, but they can be (imho) very heavy to manage and there is the possibility to use too much exceptions. And they requires additional check, maybe undesired to optimize the code.
Personally I use exceptions only whenever the code requires a deep catch control (catch statements are very low in the call stack) or whenever the function parameters are not hardcoded in the code.

横笛休吹塞上声 2024-07-11 10:07:15

异常可以被捕获并吞掉,从而使错误对测试不可见。 Debug.Assert 不会发生这种情况。

没有人应该有一个捕获所有异常的 catch 处理程序,但人们无论如何都会这样做,有时这是不可避免的。 如果您的代码是从 COM 调用的,则互操作层会捕获所有异常并将它们转换为 COM 错误代码,这意味着您不会看到未处理的异常。 断言不会受此影响。

此外,当异常未得到处理时,更好的做法是进行小型转储。 VB 比 C# 更强大的一个领域是,您可以使用异常过滤器在异常发生时捕获小型转储,并保持异常处理的其余部分不变。 Gregg Miskelly 关于异常过滤器注入的博客文章 提供了一种在 C# 中执行此操作的有用方法。

关于资产的另一注释...它们与代码中的错误条件单元测试的交互很差。 有一个包装器来关闭单元测试的断言是值得的。

An exception can be caught and swallowed making the error invisible to testing. That can't happen with Debug.Assert.

No one should ever have a catch handler that catches all exceptions, but people do it anyway, and sometimes it is unavoidable. If your code is invoked from COM, the interop layer catches all exceptions and turns them into COM error codes, meaning you won't see your unhandled exceptions. Asserts don't suffer from this.

Also when the exception would be unhandled, a still better practice is to take a mini-dump. One area where VB is more powerful than C# is that you can use an exception filter to snap a mini-dump when the exception is in flight, and leave the rest of the exception handling unchanged. Gregg Miskelly's blog post on exception filter inject provides a useful way to do this from c#.

One other note on assets ... they inteact poorly with Unit testing the error conditions in your code. It is worthwhile to have a wrapper to turn off the assert for your unit tests.

失与倦" 2024-07-11 10:07:15

我使用显式检查,在公共受保护方法上抛出异常,并在私有方法上抛出断言。

通常,显式检查可以防止私有方法看到不正确的值。 所以实际上,断言正在检查一个不可能的条件。 如果断言确实触发,它会告诉我该类的公共例程之一中包含的验证逻辑存在缺陷。

I use explicit checks that throw exceptions on public and protected methods and assertions on private methods.

Usually, the explicit checks guard the private methods from seeing incorrect values anyway. So really, the assert is checking for a condition that should be impossible. If an assert does fire, it tells me the there is a defect in the validation logic contained within one of the public routines on the class.

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