代码合同或if声明?

时间:2014-09-11 20:27:28

标签: c# if-statement code-contracts

我只是尝试使用Code Contracts,而且我发现if statement没有真正的优势。

请考虑以下事项。

private static void BindClassesToInterfacesByConvention(string classesEndingWith
    , string interfacesEndingwith) {
    Contract.Requires<ArgumentNullexception>(
        string.IsNullOrWhiteSpace(classesEndingWith)
        , "classesEndingWith");

    Contract.Requires<ArgumentNullException>(
        string.IsNullOrWhitespace(interfacesEndingWith)
        , "interfacesendingWith");

    ...
}

我觉得比简单地使用if statement

更令人困惑
private static void BindClassesToInterfacesByConvention(string classesEndingWith
    , string interfacesEndingwith) {
    if (string.IsNullOrWhiteSpace(classesEndingWith)) 
        throw new ArgumentNullException("classesEndingWith");

    if (string.IsNullOrWhitespace(interfacesEndingWith))
        throw new ArgumentNullException("interfacesendingWith");

    ...
}

代码合同应该在编译时警告我违反了合同。因此,当我写下以下内容时,我希望收到错误或警告。

BindClassesToInterfacesByConvention(null, null);

没有任何事情发生,一切都编得很好,既没有出现错误也没有出现警告信息。

在这种情况下,我认为最好继续使用it statement。或者也许这是对Code Contracts的不公平使用?

4 个答案:

答案 0 :(得分:4)

代码合同是一个很好的主意,因为工具并不存在。 首先,为了使异常实际抛出,您必须在Visual Studio中安装正确的扩展和/或在项目中配置正确的设置。如果你的单元测试依赖于在运行时抛出异常的代码契约并在构建服务器上运行它们,那就太好了。

但是,重要的是要理解代码合同的真正目的不仅仅是抛出异常。它支持静态代码分析(如果你打开它),启用它时可能会在编译时给你一个错误 - 但它确实需要你做很多工作才能将它应用到几乎所有地方以便静态代码分析真正起作用。我相信这是你想要测试的场景吗?在这种情况下,我建议您查看项目的代码合同设置,以确保您已启用所有静态代码检查(它将使您的构建相当长)。

此外,重要的是,代码契约允许您将意图传达给方法的调用者; Intellisense将了解您指定的条件(假设您安装了正确的扩展名)。有关代码协定的信息也可以自动添加到随附程序集的XML文件中,这样可以使程序集的第三方用户在编写代码时了解您的要求,并允许您包含此信息在使用Sandcastle等构建的帮助文件中。

这是一个好主意,但尚未在工具中完全实现,所以偶尔会有一些有趣的行为。就个人而言,我暂时停止使用它们。

答案 1 :(得分:3)

您正在指定要求其参数为null或空格的方法,然后传递null。合同满意。这就是为什么你没有违反合同的原因。 (Requires()要求在条件计算结果为false时抛出异常,而不是true。)

此外,即使您更正了合同,如果参数值是不包含字符或仅包含空白字符的非空字符串,则不应该抛出ArgumentNullException。在这种情况下,您应该抛出ArgumentException

我会这样做:

private static void BindClassesToInterfacesByConvention(string classesEndingWith
    , string interfacesEndingwith) {
    Contract.Requires<ArgumentNullException>(classesEndingWith != null
        , "classesEndingWith");

    Contract.Requires<ArgumentException>(!string.IsNullOrWhiteSpace(classesEndingWith)
        , "classesEndingWith");

    Contract.Requires<ArgumentNullException>(interfacesEndingWith != null
        , "interfacesEndingWith");

    Contract.Requires<ArgumentException>(!string.IsNullOrWhiteSpace(interfacesEndingWith)
        , "interfacesEndingWith");

    ...
}

要下载代码约定分析工具(包括Visual Studio集成),请访问http://visualstudiogallery.msdn.microsoft.com/1ec7db13-3363-46c9-851f-1ce455f66970

答案 2 :(得分:1)

这是一个主观的答案,但我会说这里的真实陈述是:&#34;我只是尝试使用代码合约,而且我认为没有真正优于单元测试&#34;

例如:

private void Foo(string something) 
{
    Contract.Requires<ArgumentNullException>(something != null, "something");
}

等同于(NUnit测试):

void Foo(string something) 
{
    if (something == null) 
        throw new ArgumentNullException();
}

[Test]
[ExpectedException( typeof( ArgumentNullException ) )]
void foo_throws_exception_with_null_param() 
{
    Foo(null);
}

哪个更好?从我的(有限的)经验来看,VS的静态分析插件非常慢。如果您使用明确的null变量对foo进行了调用,那么它就会将其捡起来。但它不会选择动态加载的空值并在用户迭代期间发送到foo

另一方面,如果你有一个if语句和一个单元测试来确保它会抛出一个ArgumentNullException,那么你就知道会抛出异常;并且您可以在运行时环境中处理它... 您可以测试使用Foo的任何内容以确保它处理异常。

使用NUnit确保显式检查非常快。单元测试的缺点是开始测试。所以我的观点是,随着时间的推移,你会通过明确的方式节省更多的时间,进行单元测试并确保你的应用程序可以处理如果抛出这些异常......它只会花费你更多的时间来开始设置它了。

答案 3 :(得分:0)

是的,如果启用静态检查,代码合同将在编译时警告您违反了合同。事实证明,代码合同加载项(Code Contracts for .NET)仅适用于Visual Studio 2013或更低版本,但不是Visual Studio 2015或2017。

在Visual Studio 2013上: enter image description here

在Visual Studio 2015上,我们可以从“输出”窗口中看到错误信息,但不会在“错误列表”窗口中看到错误信息。此错误已被记录并修复,但它仍然可以重现。Warnings and messages from static contract checking don't appear in the VS2015 error list enter image description here

最新更新:
安装最新的Contracts.devlab9ts.msi(目前,它只是RC版本)将解决Visual Studio 2015中的问题。 DotNet CodeContracts v.1.10.20606.1-rc2 enter image description here