我正在阅读与处理空值有关的这篇文章。
建议之一(根据 SO 帖子)是在 null 无效时使用 Assert。
我(到目前为止)在测试项目中广泛使用了空值。在普通代码(测试项目除外)中使用 Asserts 语句对我来说看起来很奇怪。
为什么-> 因为我从来没有这样用过,也从来没有读过任何关于它的书。
问题
1. 是否可以使用 Asserts 作为前提条件
2. Asserts 在检查参数和抛出 Argument___Exception 方面的优缺点
如果重要的话,我要.NET(不是java)
您可能想查看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();
}
}
这篇文章只是关于Microsoft.VisualStudio.TestTools.UnitTesting.Assert,不是Debug.Assert。
我不建议这样做,因为Assert该类位于Microsoft.VisualStudio.TestTools.UnitTesting名称空间中,它正在测试东西而不是生产工具。它们也在一个单独的程序集中,您不需要从非测试代码中引用。
ArgumentException(illegal argument, with ArgumentNullExceptionand ArgumentOutOfRangeExceptionfor further split) 和 and InvalidOperationException(illegal state) 用于条件检查。
作为替代方案,也有代码合同。见史蒂夫的回答。
它们实际上是两个不同的断言。您在单元测试中使用的 Assert 用于触发测试失败(当然,它们总是经过测试)。您在其他代码中使用的 Asserts 是一个单独的函数 ( System.Diagnostics.Debug.Assert()),在开发过程中用作帮助工具,以在不满足预期条件时提醒您。但是,这些断言仅在调试版本中进行测试。如果您进行发布构建,则断言将无效。所以它不是一个通用的错误处理工具,你不应该计划用它来捕获错误。
它只是在测试和开发过程中捕获逻辑错误,告诉您您的假设是否成立。因此,是的,它对于测试前置条件和后置条件非常有用。
请注意,这种 Assert 有点争议,因为它可能会成为您最终使用的拐杖,而不是正确的错误处理,并且由于它在发布版本中没有影响,您最终可能会发布不存在错误处理的软件。(例如,不要使用 Assert 来检查文件是否存在。它是真实世界中可能发生的真实错误,因此需要真实世界的错误处理。)但我个人认为它是一个非常有用的工具,用于测试前置条件和后置条件,而不必担心性能开销(由于测试在发布版本中被删除,它实际上是免费的,并且它在断言失败时触发调试器,而不仅仅是抛出异常)
就个人而言,如果我要在方法中处理空值,那么我肯定不会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;
}
}
}