我想我在静态合同检查工具中发现了一个错误。除了用[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));
}
}
答案 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;
}