我刚开始在一个现有的中型项目上尝试.NET 4中的CodeContracts,我很惊讶静态检查器给出了关于以下代码的编译时警告:
public class Foo
{
private readonly List<string> strs = new List<string>();
public void DoSomething()
{
// Compiler warning from the static checker:
// "requires unproven: source != null"
strs.Add("hello");
}
}
为什么CodeContracts静态检查器抱怨strs.Add(...)行? strs没有可能成为null的方法,对吗?我做错了吗?
答案 0 :(得分:8)
该字段可能标记为readonly
,但遗憾的是静态检查程序对此不够智能。因此,由于静态检查器无法自己推断strs
永远不为null,因此必须通过不变量明确告知它:
[ContractInvariantMethod]
private void ObjectInvariant() {
Contract.Invariant(strs != null);
}
答案 1 :(得分:3)
以下是有效的代码,我希望生成警告。
public class Foo
{
private readonly List<string> strs = new List<string>();
public Foo()
{
// strs is readonly, so we can assign it here but nowhere else
strs = ResultOfSomeFunction();
}
public void DoSomething()
{
// Compiler warning from the static checker:
// "requires unproven: source != null"
strs.Add("hello");
}
}
很可能他们的分析器不能确保在构造函数中没有任何内容可以改变strs
的值。或者也许你在某种程度上改变了构造函数中的strs
并且你没有意识到它。
答案 2 :(得分:1)
小修正:Pex使用Z3,一个SMT求解器,而Clousot(静态检查器代码名称)使用抽象解释和抽象域。
答案 3 :(得分:0)
我不熟悉.NET的对象初始化语义的复杂性来回答你的直接问题。但是,这里有两个提示:
null
,并不意味着它不是真的。 IOW:CC可能只是错误,您需要帮助它Contract.Assume
(或者,如果您有信心,Contract.Assert
)。有趣的是,如果您明确添加strs
永远不会null
的对象不变声明,则CC 能够证明 和因此,也可以证明strs.Add()
永远不会是空引用:
[ContractInvariantMethod]
private void StrsIsNotNull()
{
Contract.Invariant(strs != null);
}
所以,我猜我的预感#2是正确的:在这种情况下,CC只是错误。 (或者更确切地说:将C#的语义编码到定理证明器中是不完整的。)