什么时候应该使用Debug.Assert()?

时间:2020-03-06 14:40:57  来源:igfitidea点击:

我已经获得CS学位,现在已经是专业软件工程师大约一年了。我已经在C ++和C中了解断言了一段时间了,但是直到最近才完全不知道它们在Cand .NET中存在。

我们的生产代码不包含任何断言,我的问题是……

我应该在生产代码中开始使用Asserts吗?如果是这样,什么时候最合适使用它?这样做更有意义吗

Debug.Assert(val != null);

或者

if ( val == null )
    throw new exception();

解决方案

将" Debug.Assert()"放在要进行完整性检查以确保不变量的代码中的任何位置。编译Release版本(即没有DEBUG编译器常量)时,对Debug.Assert()的调用将被删除,因此不会影响性能。

在调用Debug.Assert()之前,我们仍然应该抛出异常。断言只是确保在我们仍在开发时一切都按预期进行。

我不知道它在Cand .NET中的情况如何,但是在C语言中assert()仅在使用-DDEBUG编译时才有效,如果不使用-DDEBUG进行编译,最终用户将永远看不到assert()。仅适用于开发人员。我确实经常使用它,有时更容易跟踪错误。

我不会在生产代码中使用它们。抛出异常,捕获并记录。

在asp.net中还需要小心,因为断言会显示在控制台上并冻结请求。

使用断言检查开发人员的假设,使用异常检查环境的假设。

如果我们希望在生产代码中使用Asserts(即发布版本),则可以使用Trace.Assert而不是Debug.Assert。

当然,这会增加生产可执行文件的开销。

同样,如果应用程序以用户界面模式运行,则默认情况下将显示"断言"对话框,这可能会使用户有些不安。

我们可以通过删除DefaultTraceListener来覆盖此行为:查看MSDN中Trace.Listeners的文档。

总之,

  • 广泛使用Debug.Assert可以帮助捕获Debug版本中的错误。
  • 如果在用户界面模式下使用Trace.Assert,则可能要删除DefaultTraceListener以避免使用户感到困惑。
  • 如果我们要测试的条件是应用无法处理的,那么最好抛出一个异常,以确保执行不会继续。请注意,用户可以选择忽略断言。

我们应该使用Debug.Assert来测试程序中的逻辑错误。编译器只能将语法错误通知我们。因此,我们应该明确地使用Assert语句来测试逻辑错误。就像说测试一个销售汽车的程序,只有蓝色的宝马应该获得15%的折扣。编译器无法告诉我们程序在执行此操作时逻辑上是否正确,但是assert语句可以告诉我们。

仅在要为发布版本删除支票的情况下才使用断言。请记住,如果不以调试模式进行编译,则不会触发断言。

给定null检查示例,如果该示例位于仅限内部使用的API中,则可以使用断言。如果它在公共API中,我肯定会使用显式检查并抛出。

断言用于捕获程序员()错误,而不是用户错误。仅当用户没有机会导致断言触发时,才应使用它们。例如,如果我们正在编写API,则在API用户可以调用的任何方法中,都不应使用asserts来检查参数是否不为null。但是它可以在未作为API一部分公开的私有方法中使用,以断言代码在不应接受的情况下从不传递null参数。

当我不确定时,我通常倾向于使用异常而不是断言。

如果我是你,我会做:

Debug.Assert(val != null);
if ( val == null )
    throw new exception();

或者避免重复条件检查

if ( val == null )
{
    Debug.Assert(false,"breakpoint if val== null");
    throw new exception();
}

我们应该始终使用第二种方法(引发异常)。

另外,如果我们正在生产中(并且具有发布版本),则抛出异常(使应用程序在最坏的情况下崩溃)比使用无效值可能会破坏客户的数据(可能要花费数千美元)要好。美元)。

从代码完成

8 Defensive Programming
  
  8.2 Assertions
  
  An assertion is code that’s used during development—usually a routine
  or macro—that allows a program to check itself as it runs. When an
  assertion is true, that means everything is operating as expected.
  When it’s false, that means it has detected an unexpected error in the
  code. For example, if the system assumes that a customer-information
  file will never have more than 50,000 records, the program might
  contain an assertion that the number of records is lessthan or equal
  to 50,000. As long as the number of records is less than or equal to
  50,000, the assertion will be silent. If it encounters more than
  50,000 records, however, it will loudly “assert” that there is an
  error in the program.  
  
  Assertions are especially useful in large, complicated programs and
  in high reliability programs. They enable programmers to more quickly
  flush out mismatched interface assumptions, errors that creep in when
  code is modified, and so on. 
  
  An assertion usually takes two arguments: a boolean expression that
  describes the assumption that’s supposed to be true and a message to
  display if it isn’t. 
  
  (…) 
  
  Normally, you don’t want users to see assertion messages in
  production code; assertions are primarily for use during development
  and maintenance. Assertions are normally compiled into the code at
  development time and compiled out of the code for production. During
  development, assertions flush out contradictory assumptions,
  unexpected conditions, bad values passed to routines, and so on.
  During production, they are compiled out of the code so that the
  assertions don’t degrade system performance.

在我的书中几乎从来没有。
在大多数情况下,如果我们想检查一切是否正常,则抛出异常。

我不喜欢的事实是,它使调试版本与发布版本在功能上有所不同。如果调试断言失败,但是该功能在发行版中有效,那么这有什么意义呢?当断言者离开公司很久而没人知道代码的那部分时,情况会更好。然后,我们必须花费一些时间来探索问题,以查看是否确实存在问题。如果有问题,那为什么不把人放在首位呢?

对我来说,这建议我们使用Debug.Asserts,将问题推迟到其他人那里,亲自解决。如果应该是这种情况,那么就不会抛出。

我猜可能有一些性能至关重要的场景,在这些场景中我们想优化断言,并且在这里很有用,但是我还没有遇到这样的场景。

在调试Microsoft .NET 2.0应用程序中,John Robbins在断言中有很大一部分。他的主要观点是:

  • 大胆地断言。我们永远不能有太多的断言。
  • 断言不会替代异常。异常涵盖了代码所要求的内容;断言涵盖了它所假设的事物。
  • 一个写得很好的断言不仅可以告诉我们发生了什么事情和发生在哪里(例如例外情况),还可以告诉我们原因。
  • 异常消息通常是含糊不清的,要求我们向后遍历代码以重新创建导致错误的上下文。断言可以保留发生错误时程序的状态。
  • 断言是文档的两倍,它告诉其他开发人员代码所依赖的隐含假设。
  • 断言失败时出现的对话框使我们可以将调试器添加到进程,因此可以像在其中放置断点那样在堆栈中四处戳戳。

PS:如果我们喜欢Code Complete,我建议我们在本书中继续阅读。我购买它是为了学习有关使用WinDBG和转储文件的知识,但是前半部分包含了许多技巧,可首先避免出现bug。

根据IDesign标准,我们应该

Assert every assumption.  On average, every fifth line is an assertion.
using System.Diagnostics;

object GetObject()
{...}

object someObject = GetObject();
Debug.Assert(someObject != null);

作为免责声明,我应该提到,我认为实施此IRL不可行。但这是他们的标准。