您的位置:首页 > 编程语言 > C#

《C#高级编程》读书笔记(十四):代码协定

2016-07-28 16:54 399 查看
一,代码协定

代码协定通常称作契约式编程,包括如下三个部分:

前置条件(precondiction):为了调用函数,必须为真的条件,在其违反时,函数决不调用,传递好数据是调用者的责任。

后置条件(postcondion):函数保证能做到的事情,函数完成时的状态,函数有这一事实表示它会结束,不会无休止的循环

类不变项(class invariant):从调用者的角度来看,该条件总是为真,在函数的内部处理过程中,不变项可以为变,但在函数结束后,控制返回调用者时,不变项必须为真。

二,安装插件

要使用代码协定,首先需要安装Code Contracts for .NET插件。

安装插件后,可以在项目的属性页中的Code Contracts标签来配置相关选项:



勾上"Perform Runtime Check"选项,只是可以看到右侧的下拉框有五个选项,这里分别介绍一下它们的区别:

Full表示执行所有的代码协定语句。

Pre and Post表示执行前置和后置条件检查,即Contract.Require和Contract.Ensures。

Preconditions 表示只执行前置条件检查,即Contract.Require。

ReleaseRequires 表示执行public类的public方法的前置条件检查。

None表示不执行代码协定检查,即不进行代码协定注入。

三,前置条件

前置条件检查传递给方法的参数。使用Contract类中的Requires()方法可以定义前置条件。

public static void MinMax(int min, int max)
{
Contract.Requires(max>min);
//...
}


调用:

MinMax(1,1);


因为不满足前置条件,会报出异常:



Require()方法的重载方法:

public static void Requires(bool condition);
public static void Requires(bool condition, string userMessage);
public static void Requires<TException>(bool condition) where TException : Exception;
public static void Requires<TException>(bool condition, string userMessage) where TException : Exception;


例如,使用Requires方法的泛型变体可以指定当条件不满足时,调用的异常类型。如果参数o为空,下面的协定就抛出一个ArgumentNullException异常:

public static void Preconditions(object o)
{
Contract.Requires<ArgumentNullException>(o!=null,"Preconditions,o may not be null");
}


为了检测用作参数的集合,Contract类提供了Exists()和ForAll()方法。ForAll()方法检测集合中的没一项,看看他们是否满足条件。

public static void ArrayTest(int[] data)
{
Contract.Requires(Contract.ForAll(data,i=>i<12));
}


四,后置条件

后置条件定义了方法执行完后共享数据和返回值的保证。尽管后置条件定义了关于返回值的一些保证,但他们必须放在方法的开头;所有的协定要求都必须放在方法的开头。

static void PostCondition()
{
Contract.Ensures(sharedState<6);
sharedState = 9;
Console.WriteLine($"change sharedState invariant {sharedState}");
sharedState = 3;
Console.WriteLine($"before returing change it to a valid value {sharedState}");
}


Ensures()方法的重载方法:

public static void Ensures(bool condition);
public static void Ensures(bool condition, string userMessage);


为了保证返回某个值,可以对Ensures()方法的协定使用特定的值Result<T>

static int ReturnValue()
{
Contract.Ensures(Contract.Result<int>()<6);
return 3;
}


还可以比较新旧值。为此应使用OldValue<T>()方法,它返回在方法入口给变量传递的初始值。

static int ReturnLargerThanInput(int x)
{
Contract.Ensures(Contract.Result<int>()>Contract.OldValue<int>(x));
return x + 3;
}


五,类不变项

不变量为对象生命周期中的变量定义了协定。Contract.Requires()方法定义了输入要求,Contract.Ensures()方法定义了方法结束时的要求。Contract.Invariant()方法定义了在对象整个生命周期中都必须满足的条件。对Contract.Invariant的调用只能放在应用了ContractInvariantMethod特性的方法内。

private int x = 5;
[ContractInvariantMethod]
public void ObjectInvariant()
{
Contract.Invariant(x>5);
}


部分内容参考了:代码协定(一)——简介
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: