所有的Ensures和Requires调用必须在方法或属性体中的所有其他语句之前,包括像您正在使用的简单赋值语句这样有助于可读性的语句。
适当的语法
public int? Page {
get {
Contract.Ensures(Contract.Result<int?>() == null
|| Contract.Result<int?>() >= 0);
return default(int?);
}
}
}
这很丑,比普通的if (x || y) throw new ArgumentOutOfRangeException()
要丑得多。
特殊属性
有一个迂回的方法可以解决这个问题。 ContractAbbreviatorAttribute
和ContractArgumentValidatorAttribute
是ccrewrite
理解的特殊属性,可以让您的生活更轻松。(有关详细信息,请参见MSDN上的System.Diagnostics.Contracts
命名空间文档或Code Contracts manual。)
如果使用 .NET 4 或更早版本:
这些属性从 .NET 4.5 开始出现在框架中,但是对于早期版本,您可以从 Code Contracts 安装的目录(
C:\Program Files (x86)\Microsoft\Contracts\Languages\
)中获取它们的源文件。在该文件夹下有
CSharp
和
VisualBasic
子文件夹,其中包含了一个
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
的调用。由于它只处理参数验证,它不会帮助您解决后置条件示例。