为什么这个C#代码合同格式错误?

时间:2009-12-21 01:39:15

标签: c# c#-4.0 code-contracts microsoft-contracts

Visual Studio在下面写下此合同时显示错误。

错误20方法'.... get_Page'

中的合同格式错误

'if'块有问题吗?

public int? Page
{
get
{
    int? result = Contract.Result<int?>();

    if (result != null)
        Contract.Ensures(result >= 0);

    return default(int?);
}
}

修改

Lasse V. Karisen发表评论:

怎么样:Contract.Ensures(结果== null ||结果&gt; = 0); ?

是Karisen,我之前尝试过这个并且编译。但问题仍然存在:在使用合同时是否有可能产生ifs?

我遇到的另一个问题是无能为力(主要考虑上面的例子),也涉及结果的使用:

public int IndexOf(T item)
{
    Contract.Assert(item != null);
    Contract.Assert((item as IEntity).ID > 0);

    int result = Contract.Result<int>();
    Contract.Ensures(result >= -1);

    return default(int);
}

5 个答案:

答案 0 :(得分:10)

合同格式错误,因为所有合同条款必须出现在任何其他代码之前。

答案 1 :(得分:2)

你不需要if,做布尔操作而是使用隐含!

public int? Page
{
    get
    {
        Contract.Ensures( (result!= null).Implies(result >= 0) );
        var result = ...

        ...


        return result;
    }
}

此外,在测试方法参数和其他前提条件时,您应该使用Requires not assert。

public int IndexOf(T item)
{
    Contract.Requires(item != null);
    Contract.Requires((item as IEntity).ID > 0);
...

答案 2 :(得分:1)

只是在猜测。也许它应该是Contract.Ensures(result.Value >= 0)

答案 3 :(得分:0)

合约上有条件编译标志。 在发布更多代码

if condition
    contract
return

变为

if condition
   return
你现在看到问题吗?

答案 4 :(得分:0)

所有确认和要求调用必须在方法或属性体中的所有其他语句之前,这包括简单的分配,例如您正在使用该帮助可读性。

正确的语法

public int? Page {
    get {
        Contract.Ensures(Contract.Result<int?>() == null 
            || Contract.Result<int?>() >= 0); 

        return default(int?);
        }
    }
 }

非常丑陋,比普通if (x || y) throw new ArgumentOutOfRangeException()更加丑陋。

特殊属性

有一种迂回的方式绕过这个。 ContractAbbreviatorAttributeContractArgumentValidatorAttributeccrewrite了解的特殊属性,可让您的生活更轻松。 (有关大量详细信息,请参阅MSDN上的System.Diagnostics.Contracts命名空间文档或代码合同manual。)

如果使用的是.NET 4或更早版本: 这些属性位于从.NET 4.5开始的框架中,但对于以前的版本,您可以从Code Contracts安装目录中获取它们的源文件。 (C:\Program Files (x86)\Microsoft\Contracts\Languages\)在该文件夹中,CSharpVisualBasic个子文件夹包含ContractExtensions.cs(或.vb)文件,其中包含所需的代码。

<强> ContractAbbreviatorAttribute 此属性有效地允许您创建合同宏。有了它,您的页面属性可以这样写:

public int? Page {
    get {
        EnsuresNullOrPositive();
        return default(int?)
    }
}

[ContractAbbreviator]
static void EnsuresNullOrPositive(int? x) {
    Contract.Ensures(
        Contract.Result<int?>() == null ||                 
        Contract.Result<int?>() >= 0);
}

EnsuresNullOrPositive也可以保存在静态类中,并在整个项目中重用,或者公开并放在实用程序库中。你也可以像下一个例子那样使它更加通用。

[ContractAbbreviator]
static void EnsuresNullOrPositive<Nullable<T>>(Nullable<T> obj) {
    Contract.Ensures(
        Contract.Result<Nullable<T>>() == null ||                 
        Contract.Result<Nullable<T>>() >= default(T));
}

对于我自己的实用程序库,我有一个名为Requires的静态类和一个名为Ensures的静态类,每个类都有许多用ContractAbbreviator修饰的静态方法。以下是一些例子:

public static class Requires {

    [ContractAbbreviator] 
    public static void NotNull(object obj) {  
        Contract.Requires<ArgumentNullException>(obj != null);
    }

    [ContractAbbreviator]
    public static void NotNullOrEmpty(string str) {
        Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(str));
    }

    [ContractAbbreviator]
    public static void NotNullOrEmpty(IEnumerable<T> sequence) {
        Contract.Requires<ArgumentNullException>(sequence != null);
        Contract.Requires<ArgumentNullException>(sequence.Any());
    }
}

public static class Ensures {
    [ContractAbbreviator]
    public static void NotNull(){
        Contract.Ensures(Contract.Result<object>() != null);
    }
}

这些可以这样使用:

public List<SentMessage> EmailAllFriends(Person p) {
    Requires.NotNull(p); //check if object is null
    Requires.NotNullOrEmpty(p.EmailAddress); //check if string property is null or empty
    Requires.NotNullOrEmpty(p.Friends); //check if sequence property is null or empty
    Ensures.NotNull(); //result object will not be null

    //Do stuff
}

<强> ContractArgumentValidatorAttribute 我没有在教程之外使用过这个,但基本上它允许你在一次调用中编写几个if (test) throw new ArgumentException()调用包,其行为类似于对Contract.Requires的调用。由于它只处理参数验证,因此对后置条件示例没有帮助。