技术开发 频道

.NET 4.0 中的契约式编程

  详细的解释稍后再说。按理,既然契约式编程有那么多好处,那在 C/C++ 世界应该很流行才对。为什么很少看到关于契约式编程的讨论呢?看一下 C++ 的契约式编程示例就知道了。下面是 C++ 代码示例:

//typedef long int32_t;
#include <stdint.h>

template
inline
void CheckInvariant(T& argument)
{
#ifdef CONTRACT_FULL
    argument.Invariant();
#endif
}

public class RationalNumber
{
private:
    int32_t numberator;
    int32_t denominator;

public:
    RationalNumber(int32_t numberator, int32_t denominator)
    {
#ifdef CONTRACT_FULL
        ASSERT(denominator
!= 0);
        CheckInvaraint(
*this);
#endif
        
this.numberator = numberator;
        
this.denominator = denominator;
#ifdef CONTRACT_FULL
        CheckInvaraint(
*this);
#endif
    }

public:
    int32_t GetDenominator()
    {
#ifdef CONTRACT_FULL
        
// C++ Developers like to use struct type.
        class Contract
        {
            int32_t Result;
            Contract()
            {
            }
            
~Contract()
            {
            }
        }
#endif
#ifdef CONTRACT_FULL
        Contract contract
= new Contract();
        contract.Result
= denominator;
        CheckInvairant(
*this);
#endif
        
return this.denominator;
#ifdef CONTRACT_FULL
        CheckInvaraint(
*this);
#endif
    }

protected:
#ifdef CONTRACT_FULL
    
virtual void Invariant()
    {
        
this.denominator != 0;
    }
#endif
}

  上述代码充斥了大量的宏和条件编译。对于习惯了 C# 优雅语法的 .NET 开发人员来说,它们是如此丑陋。更重要的是,契约式编程在 C++ 世界并未被标准化,因此项目之间的定义和修改各不一样,给代码造成很大混乱。这正是很少在实际中看到契约式编程应用的原因。但是在 .NET 4.0 中,契约式编程变得简单优雅起来。.NET 4.0 提供了契约式编程库。

  实际上,.NET 4.0 仅仅是针对 C++ 宏和条件编译的再次抽象和封装。它完全基于 CONTRACTS_FULL, CONTRACTS_PRECONDITIONS Symbol 和 System.Diagnostics.Debug.Assert 方法、System.Environment.FastFail 方法的封装。

  那么,何谓契约式编程?

  何谓契约式编程

  契约是减少大型项目成本的突破性技术。它一般由 Precondition(前置条件), Postcondition(后置条件) 和 Invariant(不变量) 等概念组成。.NET 4.0 除上述概念之外,还增加了 Assert(断言),Assume(假设) 概念。这可以由枚举 ContractFailureKind 类型一窥端倪。

  契约的思想很简单。它只是一组结果为真的表达式。如若不然,契约就被违反。那按照定义,程序中就存在纰漏。契约构成了程序规格说明的一部分,只不过该说明从文档挪到了代码中。开发人员都知道,文档通常不完整、过时,甚至不存在。将契约挪移到代码中,就使得程序可以被验证。

  正如前所述,.NET 4.0 对宏和条件编译进行抽象封装。这些成果大多集中在 System.Diagnostics.Contracts.Contract 静态类中。该类中的大多数成员都是条件编译。这样,我们就不用再使用 #ifdef 和定义 CONTRACTS_FULL 之类的标记。更重要的是,这些行为被标准化,可以在多个项目中统一使用,并根据情况是否生成带有契约的程序集。

  1. Assert

  Assert(断言)是最基本的契约。.NET 4.0 使用 Contract.Assert() 方法来特指断言。它用来表示程序点必须保持的一个契约。

Contract.Assert(this.privateField > 0);
Contract.Assert(
this.x == 3, "Why isn’t the value of x 3?");

  断言有两个重载方法,首参数都是一个布尔表达式,第二个方法的第二个参数表示违反契约时的异常信息。

  当断言运行时失败,.NET CLR 仅仅调用 Debug.Assert 方法。成功时则什么也不做。

  2. Assume

  .NET 4.0 使用 Contract.Assume() 方法表示 Assume(假设) 契约。

Contract.Assume(this.privateField > 0);
Contract.Assume(
this.x == 3, "Static checker assumed this");

  Assume 契约在运行时检测的行为与 Assert(断言) 契约完全一致。但对于静态验证来说,Assume 契约仅仅验证已添加的事实。由于诸多限制,静态验证并不能保证该契约。或许最好先使用 Assert 契约,然后在验证代码时按需修改。

  当 Assume 契约运行时失败时, .NET CLR 会调用 Debug.Assert(false)。同样,成功时什么也不做。

0
相关文章