到目前为止,我已经做了一年的专业软件工程师,并获得了计算机科学学位。我知道c++和C中的断言有一段时间了,但直到最近才知道它们存在于c#和。net中。

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

我应该开始在我们的生产代码中使用断言吗?如果可以,什么时候使用它最合适?这样做会更有意义吗

Debug.Assert(val != null, "message");

or

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

当前回答

摘自Code Complete (Wikipedia):

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。断言可以是正确的选择。

1)我在这里没有看到提到的是assert在自动化测试期间可以提供的额外概念覆盖。举个简单的例子:

当作者认为他们已经扩展了代码的范围以处理额外的场景,并修改了一些高级调用者时,理想情况下(!)他们将编写单元测试来覆盖这种新情况。那么,完全集成的代码可能会表现得很好。

然而,实际上已经引入了一个微妙的缺陷,但在测试结果中没有发现。在这种情况下,被调用者变得不确定,只提供预期的结果。或者,它产生了一个未被注意到的舍入误差。或者造成的错误在其他地方被抵消。或者不仅授予所请求的访问权限,还授予不应该授予的其他特权。等。

此时,被调用者中包含的Debug.Assert()语句加上单元测试驱动的新用例(或边缘用例)可以在测试期间提供宝贵的通知,即原始作者的假设已经失效,并且在没有额外审查的情况下不应该发布代码。断言与单元测试是完美的合作伙伴。

2)此外,有些测试写起来很简单,但成本很高,而且在初始假设下是不必要的。例如:

如果一个对象只能从某个安全的入口点访问,是否应该从每个对象方法对网络权限数据库进行额外查询,以确保调用者具有权限?当然不是。也许理想的解决方案包括缓存或其他一些功能扩展,但设计并不需要这样做。当对象被附加到不安全的入口点时,Debug.Assert()将立即显示。

3)其次,在某些情况下,当以发布模式部署时,您的产品可能对其全部或部分操作没有有用的诊断交互。例如:

Suppose it is an embedded real-time device. Throwing exceptions and restarting when it encounters a malformed packet is counter-productive. Instead the device may benefit from best-effort operation, even to the point of rendering noise in its output. It also may not have a human interface, logging device, or even be physically accessible by human at all when deployed in release mode, and awareness of errors is best provided by assessing the same output. In this case, liberal Assertions and thorough pre-release testing are more valuable than exceptions.

4)最后,有些测试是不必要的,只是因为被呼叫方被认为是非常可靠的。在大多数情况下,代码的可重用性越高,在使其可靠方面投入的精力就越多。因此,对于来自调用方的意外参数,通常使用Exception,而对于来自被调用方的意外结果,则使用Assert。例如:

If a core String.Find operation states it will return a -1 when the search criteria is not found, you may be able to safely perform one operation rather than three. However, if it actually returned -2, you may have no reasonable course of action. It would be unhelpful to replace the simpler calculation with one that tests separately for a -1 value, and unreasonable in most release environments to litter your code with tests ensuring core libraries are operating as expected. In this case Asserts are ideal.

您应该使用Debug。断言来测试程序中的逻辑错误。编译器只能通知您语法错误。因此,您肯定应该使用Assert语句来测试逻辑错误。比如测试一个销售汽车的项目,只有蓝色的宝马可以得到15%的折扣。编译器不能告诉你你的程序在执行这个操作时逻辑上是否正确,但是assert语句可以。

所有的断言应该是代码,可以优化为:

Debug.Assert(true);

因为它检验的是你已经假设为真的东西。例如:

public static void ConsumeEnumeration<T>(this IEnumerable<T> source)
{
  if(source != null)
    using(var en = source.GetEnumerator())
      RunThroughEnumerator(en);
}
public static T GetFirstAndConsume<T>(this IEnumerable<T> source)
{
  if(source == null)
    throw new ArgumentNullException("source");
  using(var en = source.GetEnumerator())
  {
    if(!en.MoveNext())
      throw new InvalidOperationException("Empty sequence");
    T ret = en.Current;
    RunThroughEnumerator(en);
    return ret;
  }
}
private static void RunThroughEnumerator<T>(IEnumerator<T> en)
{
  Debug.Assert(en != null);
  while(en.MoveNext());
}

在上面,有三种不同的空参数方法。第一个接受它为允许的(它只是什么都不做)。第二个则抛出异常供调用代码处理(或不处理,导致错误消息)。第三种假设它不可能发生,并断言它是这样的。

在第一种情况下,没有问题。

在第二种情况下,调用代码有一个问题——它不应该用null调用GetFirstAndConsume,所以它会返回一个异常。

在第三种情况下,这段代码有一个问题,因为在调用它之前,它应该已经检查过en != null,所以它不是真的是一个错误。或者换句话说,它应该是理论上可以优化为Debug.Assert(true)的代码,sicne en != null应该始终为真!

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

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

根据设计标准,你应该

坚持每一个假设。平均每五行就有一个断言。

using System.Diagnostics;

object GetObject()
{...}

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

作为一个免责声明,我应该提到我还没有发现实现这个IRL是实际的。但这是他们的标准。