5

我正在阅读与处理空值有关的这篇文章。

建议之一(根据 SO 帖子)是在 null 无效时使用 Assert。

我(到目前为止)在测试项目中广泛使用了空值。在普通代码(测试项目除外)中使用 Asserts 语句对我来说看起来很奇怪。

为什么-> 因为我从来没有这样用过,也从来没有读过任何关于它的书。

问题
1. 是否可以使用 Asserts 作为前提条件
2. Asserts 在检查参数和抛出 Argument___Exception 方面的优缺点

如果重要的话,我要.NET(不是java)

4

4 回答 4

8

您可能想查看Code Contracts。它们为您的方法提供静态和运行时检查,您也可以将它们应用到您的接口,以便合约成为您的公共 API 的一部分。

例如,从我自己的一些代码中复制而来(此代码在接口上实现合约):

using System;
using System.Diagnostics.Contracts;

using Project.Contracts.Model.Accounts;
using Project.Contracts.Services;

/// <summary>
/// Data access for accounts.
/// </summary>
[ContractClass(typeof(AccountRepositoryContract))]
public interface IAccountRepository
{
    /// <summary>
    /// Gets the user by id.
    /// </summary>
    /// <param name="userId">The user id.</param>
    /// <returns>The user, or <c>null</c> if user not found.</returns>
    [Pure]
    User GetUserById(int userId);
}

/// <summary>
/// Contract class for <see cref="IAccountRepository"/>.
/// </summary>
[ContractClassFor(typeof(IAccountRepository))]
internal abstract class AccountRepositoryContract : IAccountRepository
{
    /// <summary>
    /// Gets the user by id.
    /// </summary>
    /// <param name="userId">The user id.</param>
    /// <returns>
    /// The user, or <c>null</c> if user not found.
    /// </returns>
    public User GetUserById(int userId)
    {
        Contract.Requires<ArgumentException>(userId > 0);

        return null;
    }
}

一个更简单但更全面的示例:

public class Foo
{
    public String GetStuff(IThing thing)
    {
        // Throws ArgumentNullException if thing == null
        Contract.Requires<ArgumentNullException>(thing != null);

        // Static checking that this method never returns null
        Contract.Ensures(Contract.Result<String>() != null);

        return thing.ToString();
    }
}
于 2012-12-19T16:52:53.447 回答
2

这篇文章只是关于Microsoft.VisualStudio.TestTools.UnitTesting.Assert,不是Debug.Assert

我不建议这样做,因为Assert该类位于Microsoft.VisualStudio.TestTools.UnitTesting名称空间中,它正在测试东西而不是生产工具。它们也在一个单独的程序集中,您不需要从非测试代码中引用。

ArgumentException(illegal argument, with ArgumentNullExceptionand ArgumentOutOfRangeExceptionfor further split) 和 and InvalidOperationException(illegal state) 用于条件检查。

作为替代方案,也有代码合同。见史蒂夫的回答

于 2012-12-19T16:51:29.950 回答
2

它们实际上是两个不同的断言。您在单元测试中使用的 Assert 用于触发测试失败(当然,它们总是经过测试)。您在其他代码中使用的 Asserts 是一个单独的函数 ( System.Diagnostics.Debug.Assert()),在开发过程中用作帮助工具,以在不满足预期条件时提醒您。但是,这些断言在调试版本中进行测试。如果您进行发布构建,则断言将无效。所以它不是一个通用的错误处理工具,你不应该计划用它来捕获错误。

它只是在测试和开发过程中捕获逻辑错误,告诉您您的假设是否成立。因此,是的,它对于测试前置条件和后置条件非常有用。

请注意,这种 Assert 有点争议,因为它可能会成为您最终使用的拐杖,而不是正确的错误处理,并且由于它在发布版本中没有影响,您最终可能会发布不存在错误处理的软件。(例如,不要使用 Assert 来检查文件是否存在。它是真实世界中可能发生的真实错误,因此需要真实世界的错误处理。)但我个人认为它是一个非常有用的工具,用于测试前置条件和后置条件,而不必担心性能开销(由于测试在发布版本中被删除,它实际上是免费的,并且它在断言失败时触发调试器,而不仅仅是抛出异常)

于 2012-12-19T16:55:31.350 回答
0

就个人而言,如果我要在方法中处理空值,那么我肯定不会assert在所述方法中使用 an 。相反,正如您所说,抛出异常。

本质上,当断言失败时——无论如何它基本上都是一个例外。如果你要抛出一个ArgumentNullException我会避免在方法本身中使用 try catch 来做任何事情,而是在你的单元测试中处理它。

public class A
{
  public double Add(double a, double b)
  {
    if (a == null || b == null)
      throw new ArgumentNullException("");
    return a + b;
  }
}

[TestClass()]
public UnitTest
{
  [TestMethod()]
  public void ATest()
  {
    try
    {
      double d = new A().Add(null, 1);
    }
    catch(ArgumentNullException ex)
    {
      //Fail the test
      Assert.Fail("");
      //Or throw, this will allow you to double click the exception in the unit test and go to the line of code which failed
      throw ex;
    }  
  }
}
于 2012-12-19T16:58:34.920 回答