C# 可以使用断言作为前提条件吗?

C# 可以使用断言作为前提条件吗?,c#,.net,null,assert,illegalargumentexception,C#,.net,Null,Assert,Illegalargumentexception,我正在讨论与空值处理相关的问题 其中一个建议(根据SO帖子)是在null无效时使用Assert 到目前为止,我已经在测试项目中广泛使用了空值。在普通代码中使用Asserts语句(测试项目除外)对我来说很奇怪 为什么->因为我从来没有这样使用过它,也从来没有读过任何关于它的书 问题 1.对前提条件使用断言可以吗 2.断言对检查参数和引发参数异常的利弊 如果有关系的话,我要的是.NET(不是java)这篇文章只是关于Microsoft.VisualStudio.TestTools.UnitTesti

我正在讨论与空值处理相关的问题

其中一个建议(根据SO帖子)是在null无效时使用Assert

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

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

问题
1.对前提条件使用断言可以吗
2.断言对检查参数和引发参数异常的利弊

如果有关系的话,我要的是.NET(不是java)

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

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

ArgumentException
(非法参数,带有
ArgumentNullException
ArgumentOutOfRangeException
,用于进一步拆分)和
InvalidOperationException
(非法状态)用于条件检查


作为替代方案,还有代码契约。请参阅。

您可能需要查看。它们提供了对方法的静态和运行时检查,您也可以将它们应用于接口,以便契约成为公共API的一部分

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

使用系统;
使用System.Diagnostics.Contracts;
使用Project.Contracts.Model.Accounts;
使用项目、合同、服务;
/// 
///帐户的数据访问。
/// 
[合同类别(类型(AccountRepositoryContract))]
公共接口存储库
{
/// 
///按id获取用户。
/// 
///用户id。
///用户,如果找不到用户,则为null。
[纯]
用户GetUserById(int userId);
}
/// 
///合同班。
/// 
[ContractClassFor(类型(IAccountRepository))]
内部抽象类AccountRepositoryContract:IAccountRepository
{
/// 
///按id获取用户。
/// 
///用户id。
/// 
///用户,如果找不到用户,则为null。
/// 
公共用户GetUserById(int userId)
{
合同。需要(用户ID>0);
返回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();
    }
}
公共类Foo
{
公共字符串GetStuff(i字符串)
{
//如果thing==null,则引发ArgumentNullException
Contract.Requires(thing!=null);
//静态检查此方法是否从不返回null
Contract.sure(Contract.Result()!=null);
返回thing.ToString();
}
}

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

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


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

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

本质上,当断言失败时,它基本上是一个异常。如果要抛出
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;
    }  
  }
}

Assert
方法是否受调试开关的制约(通过
[条件(“调试”)]
,因此只在开发时有用?有一个
DEBUG.Assert
和UnitTesting.Assert`。它们是完全分开的,前者用于单元测试之外。:)可以代替try/catch使用更简洁的语法。@Tilak谢谢!我不知道:)该属性如何处理异常?它是否会使测试失败或显示异常,这样当您双击它时,它将命中发生行?+1如果目的是测试方法中的假设(例如,in参数有值),那么代码契约就是一条路,你可能也不想去看它,因为即使它不是一个有缺陷的库,你也真的不需要另一个复杂和摩擦的来源,而且几乎没有或根本没有任何实质性的好处。
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;
    }  
  }
}