C#代码协定Contract静态类

Contract 类

静态类Contract

参考文档:   代码协定Contract

命名空间:

System.Diagnostics.Contracts

包含用于表示程序协定(如前置条件Preconditions、后置条件Postconditions 和对象固定Invariants)的静态方法。

注解

代码协定类使你可以在代码中指定前置条件、后置条件和对象固定。 

前置条件是输入方法或属性时必须满足的要求。 

后置条件描述在方法或属性代码退出时的预期。 

对象固定条件描述了不存在任何条件问题的类的预期状态。对象固定描述处于良好状态的类的预期状态。

代码协定包含用于标记代码的类、用于编译时分析的静态分析器以及运行时分析器。 

代码协定包括以下优点:

  • 改进测试:代码协定提供静态协定验证、运行时检查和文档生成。

  • 自动测试工具:可通过过滤掉不满足前置条件的无意义的测试参数,使用代码协定来生成更有意义的单元测试。

  • 静态验证:静态检查器无需运行程序即可决定是否存在任何协定冲突。 它可检查隐式协定(如 null 取消引用和数组绑定)和显式协定。

  • 参考文档:文档生成器扩充具有协定信息的现有 XML 文档文件。 还提供了可与 Sandcastle 一起使用的样式表,因此,生成的文档页具有协定部分。

所有 .NET Framework 语言可立即使用协定;无需编写特定分析器或编译器。 Visual Studio 外接程序使你可以指定要执行的代码协定分析的级别。 分析器可确认协定的格式是否标准(类型检查和名称解析),并且可以 Microsoft 中间语言 (MSIL) 格式生成约定的已编译形式。 通过在 Visual Studio 中创建协定,你可以使用工具提供的标准 IntelliSense。

协定类中的大多数方法都进行条件编译;即,编译器仅在你定义特殊符号 CONTRACTS_FULL 时才使用 #define 指令发出对这些方法的调用。 借助 CONTRACTS_FULL,你无需需使用 #ifdef 指令就可将协定写入代码;还可生成两种不同的版本:一种带有协定,一种未带协定。

Preconditions前置条件

可使用 Contract.Requires 方法表达前置条件。 前置条件在方法被调用时指定状态。 它们通常用于指定有效的参数值。 前置条件中提到的所有成员至少都必须与方法本身一样可以访问;否则,方法的调用方可能无法理解此前置条件。 条件必须无副作用。 运行时分析器确定前置条件失败时的运行时行为。

例如,以下前置条件表示参数 args 必须为非 null。

Contract.Requires(args != null);

如果你的代码必须在前置条件失败时引发特定异常,可使用 Requires 的泛型重载,如下所示。

string s = null;
Contract.Requires<ArgumentNullException>(s != null, "参数不能为空");

if-then-throw 语句显示为此形式时,工具会将其识别为旧的 requires 语句。 如果 if-then-throw 序列后未接其他协定,则代码以 Contract.EndContractBlock 方法结束。

if (x == null) throw new ... Contract.EndContractBlock(); // All previous "if" checks are preconditions

请注意,上述测试中的条件是取反的前置条件。 (实际前提条件为 x != null 。)求反的前置条件受到严格限制:必须按前面的示例所示编写:也就是说,它不应包含 else 子句,并且子句的主体 then 必须是单个 throw 语句。 if 测试受纯度和可见性规则约束(请参阅使用准则),但 throw 表达式仅受纯度规则约束。 但是,引发的异常类型必须与发生协定的方法同样可见。

Postconditions

后置条件是方法终止时的方法的状态协定。 刚好退出方法前检查后置条件。 运行时分析器确定后置条件失败时的运行时行为。

与前置条件不同,后置条件可能引用可见性较低的成员。 客户端可能无法理解或利用后置条件使用私有状态表达的某些信息,但这不影响客户端正确使用方法的能力。

Contract.Ensures(this.F > 0);

Contract.EnsuresOnThrow<T>(this.F > 0);

固定协定

对象固定是指无论何时对象对客户端可见都应适合类的每个实例的条件。 它们表示对象被视为正确的条件。

固定协定方法的标识方式是以 ContractInvariantMethodAttribute 属性进行标记。 除了 Invariant 方法的调用序列(其中每个调用指定一个固定协定),固定协定方法不可包含任何代码,如以下示例所示。

使用准则

协定顺序

下表显示编写方法协定时应使用的元素顺序。

协定顺序
If-then-throw statements 向后兼容的公共前置条件
Requires 所有公共前置条件。
Ensures 所有的公共(正常)后置条件。
EnsuresOnThrow 所有的公共(正常)后置条件。
Ensures 所有的私有/内部(正常)后置条件。
EnsuresOnThrow 所有的私有/内部(正常)后置条件。
EndContractBlock 如果使用没有任何其他协定的 if-then-throw 样式前置条件,请调用 EndContractBlock 以表示之前所有的 if 检查均为前置条件。

纯度

协定中调用的所有方法都必须是纯方法;即,它们不能更新任何预存在的状态。 纯方法可修改输入纯方法后创建的对象。

目前代码协定工具假定以下代码元素为纯元素:

  • 标记有 PureAttribute 的方法。

  • 标记有 PureAttribute(此属性适用于所有类型的方法)的类型。

  • 属性 get 访问器。

  • 运算符(名称以“op”开头且具有一个或两个参数和非 void 返回类型的静态方法)。

  • 完全限定名以“System.Diagnostics.Contracts.Contract”、“System.String”、“System.IO.Path”或“System.Type”开头的所有方法。

  • 任何调用的委托,前提条件是委托类型本身具有 PureAttribute 属性。 委托类型 System.Predicate<T> 和 System.Comparison<T> 被视为纯类型。

方法

 
Assert(Boolean)

检查条件;如果条件为 false,则遵循为分析器设置的升级策略。

Assert(Boolean, String)

检查条件;如果条件为 false,则遵循由分析器设置的升级策略,并显示指定的消息。

Assume(Boolean)

指示代码分析工具假设指定的条件为 true(即使无法静态地证明该条件始终为 true)。

Assume(Boolean, String)

指示代码分析工具假设某个条件为 true(即使无法静态地证明该条件始终为 true),并在假设失败时显示一条消息。

EndContractBlock()

当方法的协定仅包含 if-then-throw 形式的前置条件时,标记协定部分的结尾。

Ensures(Boolean)

为封闭方法或属性指定一个后置条件协定。

Ensures(Boolean, String)

为提供的退出条件指定后置条件协定,并指定条件为 false 时要显示的消息。

EnsuresOnThrow<TException>(Boolean)

基于提供的异常和条件为封闭方法或属性指定一个后置条件协定。

EnsuresOnThrow<TException>(Boolean, String)

基于提供的异常和条件为封闭方法或属性指定后置条件协定,并指定条件为 false 时要显示的消息。

Exists(Int32, Int32, Predicate<Int32>)

确定指定的测试对某个整数范围中的任何整数是否都为 true。

Exists<T>(IEnumerable<T>, Predicate<T>)

确定函数中是否存在某个元素集合中的元素。

ForAll(Int32, Int32, Predicate<Int32>)

确定某个特定条件是否对指定范围内的所有整数都有效。

ForAll<T>(IEnumerable<T>, Predicate<T>)

确定函数中是否存在某个集合中的所有元素。

Invariant(Boolean)

为封闭方法或属性指定一个固定的协定。

Invariant(Boolean, String)

为封闭方法或属性指定一个固定协定,并在该协定的条件失败时显示一条消息。

OldValue<T>(T)

表示方法或属性开始时的值。

Requires(Boolean)

为封闭方法或属性指定一个前置条件协定。

Requires(Boolean, String)

为封闭方法或属性指定一个前置条件协定,并在该协定的条件失败时显示一条消息。

Requires<TException>(Boolean)

为封闭方法或属性指定一个前置条件协定,并在该协定的条件失败时引发异常。

Requires<TException>(Boolean, String)

为封闭方法或属性指定一个前置条件协定,并在该协定的条件失败时引发包含提供的消息的异常。

Result<T>()

表示一个方法或属性的返回值。

ValueAtReturn<T>(T)

表示从一个方法返回时 out 参数的最终(输出)值。

事件

 
ContractFailed

协定失败时发生。

代码示例:


#define CONTRACTS_FULL
using System;
using System.Diagnostics.Contracts;
namespace ContractDemo
{
    class Program : IArrayContract
    {
        static void Main(string[] args)
        {
            //参考文档
            //https://docs.microsoft.com/zh-cn/dotnet/api/system.diagnostics.contracts.contract?view=net-5.0
            
            IArrayContract arrayContract = new Program();
            Console.WriteLine(arrayContract.Count);

            string s = null;
            Contract.Requires<ArgumentNullException>(s != null, "参数不能为空");
            Console.WriteLine("Hello");
            Console.ReadLine();
        }
    }

    // An IArray is an ordered collection of objects.
    [ContractClass(typeof(IArrayContract))]
    public interface IArray
    {
        // The Item property provides methods to read and edit entries in the array.
        Object this[int index]
        {
            get;
            set;
        }

        int Count
        {
            get;
        }

        // Adds an item to the list.
        // The return value is the position the new element was inserted in.
        int Add(Object value);

        // Removes all items from the list.
        void Clear();

        // Inserts value into the array at position index.
        // index must be non-negative and less than or equal to the
        // number of elements in the array.  If index equals the number
        // of items in the array, then value is appended to the end.
        void Insert(int index, Object value);

        // Removes the item at position index.
        void RemoveAt(int index);
    }

    [ContractClassFor(typeof(IArray))]
    internal abstract class IArrayContract : IArray
    {
        int IArray.Add(Object value)
        {
            // Returns the index in which an item was inserted.
            Contract.Ensures(Contract.Result<int>() >= -1);
            Contract.Ensures(Contract.Result<int>() < ((IArray)this).Count);
            return default(int);
        }
        Object IArray.this[int index]
        {
            get
            {
                Contract.Requires(index >= 0);
                Contract.Requires(index < ((IArray)this).Count);
                return default(int);
            }
            set
            {
                Contract.Requires(index >= 0);
                Contract.Requires(index < ((IArray)this).Count);
            }
        }
        public int Count
        {
            get
            {
                Contract.Requires(Count >= 0);
                Contract.Requires(Count <= ((IArray)this).Count);
                return default(int);
            }
        }

        void IArray.Clear()
        {
            Contract.Ensures(((IArray)this).Count == 0);
        }

        void IArray.Insert(int index, Object value)
        {
            Contract.Requires(index >= 0);
            Contract.Requires(index <= ((IArray)this).Count);  // For inserting immediately after the end.
            Contract.Ensures(((IArray)this).Count == Contract.OldValue(((IArray)this).Count) + 1);
        }

        void IArray.RemoveAt(int index)
        {
            Contract.Requires(index >= 0);
            Contract.Requires(index < ((IArray)this).Count);
            Contract.Ensures(((IArray)this).Count == Contract.OldValue(((IArray)this).Count) - 1);
        }
    }
}
 

猜你喜欢

转载自blog.csdn.net/ylq1045/article/details/112565508