代码契约 - 静态检查器不能获得可空类型?

时间:2012-07-27 10:23:56

标签: c# .net nullable code-contracts verification

我想我在静态合同检查工具中发现了一个错误。除了用[ContractVerification(false)]标记整个事物之外还有其他方法吗?

class Employee
{
    public int? HierarchyLevel { get; private set; }

    public Employee(int? level)
    {
        Contract.Requires<ArgumentException>(
            (!level.HasValue) 
            || 
            level >= 0 && level <= 10);
        Contract.Ensures(
            (!HierarchyLevel.HasValue) 
            || 
            (HierarchyLevel.Value >= 0 && HierarchyLevel.Value <= 10));
        this.HierarchyLevel = level; //unproven

        // this doesnt work either
        //if (!level.HasValue) {
        //    this.HierarchyLevel = new Nullable<int>();
        //} else {
        //    this.HierarchyLevel = new Nullable<int>(level.Value);
        //}

        // can't even make the static checker shut up with that:
        //Contract.Assume(
        //    (!HierarchyLevel.HasValue) 
        //    || 
        //    (HierarchyLevel.Value >= 0 && HierarchyLevel.Value <= 10));
    }
}

令人惊讶的是,以下版本有效。但我不想开始编写单一的代码或介绍我自己的Nullable版本,只是为了取悦合同验证工具。

class Employee2
{
    public int HierarchyLevel { get; private set; }
    public bool HasLevel { get; private set; }
    public Employee2(int level, bool hasLevel)
    {
        Contract.Requires<ArgumentException>(!hasLevel || level >= 0 && level <=10);
        if (hasLevel) {
            HasLevel = true;
            HierarchyLevel = level;
        } else {
            HasLevel = false;
            HierarchyLevel = -1;
        }
    }
    [ContractInvariantMethod]
    private void ObjectInvariant()
    {
        Contract.Invariant(
            (!HasLevel) || 
            (HierarchyLevel >= 0 && HierarchyLevel <= 10));
    }
}

1 个答案:

答案 0 :(得分:1)

首先,您在代码中放错了一些大括号(但这并没有解决您的问题):

Contract.Requires<ArgumentException>(!level.HasValue
    || (level.Value >= 0 && level.Value <= 10));
Contract.Ensures(!HierarchyLevel.HasValue
    || (HierarchyLevel.Value >= 0 && HierarchyLevel.Value <= 10));

我认为你可能是对的:静态检查器无法证明一切,也取决于基类库。 this thread中的人似乎在谈论静态检查器如何无法正确理解可空类型,这是由于一般可空类型的契约实现中的错误。

当然,您也可以编写以下内容,以解决您的问题:

Contract.Requires<ArgumentException>(!level.HasValue
    || (level.Value >= 0 && level.Value <= 10);
Contract.Ensures(HierarchyLevel == level);

这是您问题的另一种解决方案。将条件放入单独的方法中,并使用PureAttribute标记方法(表明该方法没有副作用)。然后,对传入的参数和确保的值应用该方法,如下所示:

[Pure]
public static bool IsInRange(int? value)
{
    return !value.HasValue
        || (value >= 0 && value <= 10);
}

public Employee(int? level)
{
    Contract.Requires<ArgumentException>(IsInRange(level));
    Contract.Ensures(IsInRange(HierarchyLevel));
    this.HierarchyLevel = level;
}
相关问题