所有 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
(有关详细信息,请参阅System.Diagnostics.Contracts
MSDN 上的命名空间文档或代码合同手册。)
如果使用 .NET 4 或更早版本:
这些属性在从 .NET 4.5 开始的框架中,但对于以前的版本,您可以从安装代码合同的目录中获取它们的源文件。(C:\Program Files (x86)\Microsoft\Contracts\Languages\
) 在该文件夹中是CSharp
包含所需代码VisualBasic
的(或 .vb)文件的子文件夹ContractExtensions.cs
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
}
ContractArgumentValidatorAttributeif (test) throw new ArgumentException()
我没有在教程之外使用过这个,但基本上它允许您在一个调用中
编写多个调用,其行为类似于对Contract.Requires
. 由于它仅处理参数验证,因此对您的后置条件示例没有帮助。