Debug.Assert与特定的抛出exception

我刚刚开始浏览John Robbins的“debuggingMS .Net 2.0应用程序”,并且因为Debug.Assert(…)的传福音而感到困惑。

他指出,良好实施的Asserts存储状态,有些时候是错误的,例如:

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

现在,我个人觉得他很喜欢重复他的testing,而没有一个明智的“商业逻辑”评论,也许“我不应该因为stream水漫游的过程而发生”。

所以,我认为我认为Asserts是一种低级别的“让我们保护我的假设”的东西…假设一个人认为这是一个testing,只需要在debugging中做 – 即你保护自己免受同事和未来的程序员,并希望他们实际上testing的东西。

但是我得不到的是,他接着说,除了正常的error handling之外,你还应该使用断言。 现在我想象的是这样的:

 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()); } 

我通过debugging获得了什么?请重复错误条件testing? 我想如果我们正在谈论一个非常重要的计算的debugging双重检查,我会得到它…

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

…但是我不明白参数testing肯定值得检查(在DEBUG和Release版本中)。 我错过了什么?

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

断言是为了testing“不可能”的情况,也就是说,你(在你的程序逻辑中) 假定的事情是真实的。 这些断言是告诉你这些假设是否因任何原因被打破。

希望这可以帮助!

有一个沟通方面来断言vsexception抛出。

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

如果ToString是这样实现的:

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

它说,名称不应该是空的,如果是的话,User类有一个错误。

如果ToString是这样实现的:

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

它说调用者正在使用ToString不正确,如果名称为空,并应在调用之前检查。

两者的实现

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

说如果Name为null,User类中存在bug,但我们仍然想要处理它。 (用户在调用之前不需要检查姓名。)我认为这是Robbins推荐的那种安全。

在考虑关于testing问题时,提供有关debugging与断言的指导,我已经想了很长时间了。

你应该能够以错误的input,错误的状态,无效的操作顺序和任何其他可能的错误条件来testing你的类,并且断言不应该出错。 每个断言都是检查什么东西总是应该是真实的,不pipeinput或计算执行。

我已经到达的好的经验法则:

  1. 断言不能取代function正确,独立于configuration的健壮代码。 它们是互补的。

  2. 在unit testing运行期间,即使input无效值或testing错误条件,绝对不能断开。 代码应该处理这些条件而不会发生断言。

  3. 如果断言(无论是在unit testing还是在testing过程中),这个类都会被窃听。

对于所有其他错误(通常是环境错误(networking连接丢失)或误用(调用者传递一个空值)),使用严格的检查和例外会更好,更容易理解。 如果发生exception,主叫方知道可能是他们的错。 如果发生断言,调用者知道这可能是代码中声明所在的错误。

关于重复:我同意。 我不明白为什么要用Debug.Assert和exception检查复制validation。 这不仅增加了一些噪声,而且还污染了水域中的谁是错误的,但它是一种重复的forms。

我使用明确的检查,抛出公共受保护方法的exception和私有方法的断言。

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

一个exception可以被捕获和吞下,使得testing中看不见的错误。 这不会发生Debug.Assert。

没有人应该有一个能够捕捉所有exception情况的捕捉处理程序,但是人们仍然会这样做,有时这是不可避免的。 如果您的代码是从COM调用的,则互操作层捕获所有exception并将其转换为COM错误代码,这意味着您将看不到未处理的exception。 断言不会受到这个。

另外,当例外情况未被处理时,更好的做法是采取小型转储。 VB比C#更强大的一个领域是,当exception处于运行状态时,您可以使用exceptionfilter捕捉小型转储,并保持其余的exception处理不变。 Gregg Miskelly的关于exceptionfilter注入的博客文章提供了一个有用的方法来从c#中完成这个任务。

关于资产的其他说明…他们与unit testing您的代码中的错误条件差强人意。 有一个封装来closures你的unit testing的断言是值得的。

海事组织只是一个开发时间的损失。 正确实施的例外可以让你清楚地了解发生的事情。 我看到太多的应用程序显示晦涩的“断言失败:我<10”的错误。 我认为断言是暂时的解决办法。 在我看来,在程序的最终版本中不应该有任何断言。 在我的练习中,我使用断言进行快速和肮脏的检查。 代码的最终版本应考虑到错误的情况并相应地执行。 如果有什么不好的事情发生,你有两个select:处理它或离开它。 函数应该抛出一个有意义的描述的exception,如果错误的参数传入。我认为没有重复validation逻辑的要点。

良好使用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 

我个人认为, 只有当你知道什么东西超出了理想的范围时才应该使用断言,但是你可以确定继续是合理安全的。 在所有其他情况下(我觉得没有想到的情况下可以指出)使用例外来强制失败。

对于我来说,重要的折衷是你是否想用一个exception来降低实时/生产系统,以避免腐败并使故障排除更容易,或者是否遇到了在testing/debugging版本中永远不被允许继续不被察觉的情况,被允许继续生产(当然logging警告)。

比照 http://c2.com/cgi/wiki?FailFast从java复制和修改问题:; Exception Vs Assertion

这是2美分。

我认为最好的方法是使用断言和例外。 这两个方法之间的主要区别是,如果Assert语句可以很容易地从应用程序文本(定义,条件属性…)中移除,而抛出的Exception依赖于(最终)一个条件代码,这是很难删除具有预处理器条件的多段)。

每个应用程序exception都应该正确处理,而只有在algorithm开发和testing过程中才能满足断言。

如果传递一个空对象引用作为例程参数,并使用此值,则会得到一个空指针exception。 确实:为什么你应该写一个断言? 在这种情况下浪费时间。 但是class级成员在课堂上使用的私人class级成员呢? 当这些值设置在某个地方时,如果设置了空值,最好检查一个断言。 这只是因为当你使用成员,你得到一个空指针exception,但你不知道如何设置值。 这导致重新启动程序,打破所有入口点的使用来设置私有成员。

exception是更有用的,但他们可以(imho)非常重的pipe理,并有可能使用太多的例外。 而且他们需要额外的检查,可能不希望优化代码。 就我个人而言,只有代码需要深度捕获控制(catch语句在调用堆栈中非常低)或者函数参数在代码中没有被硬编码时,我才会使用exception。