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

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

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

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

or

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

当前回答

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

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应该始终为真!

其他回答

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

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

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

如果我是你,我会这样做:

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

或者避免重复条件检查

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

断言用于捕获程序员(您的)错误,而不是用户错误。只有在用户不可能触发断言时才应该使用它们。例如,如果你正在编写一个API,在API用户调用的任何方法中,都不应该使用断言来检查参数是否为空。但是它可以在一个私有方法中使用,而不是作为API的一部分公开,以断言您的代码永远不会在不应该传递null参数时传递null参数。

当我不确定时,我通常更喜欢异常而不是断言。

我想再添加四种情况,其中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.