我正在与CodeContracts static analysis tool进行争论。
我的代码:
Screenshot http://i40.tinypic.com/r91zq9.png
该工具告诉我instance.bar
可能是空引用。我相信相反。
谁是对的?我怎么能证明它错了?
答案 0 :(得分:2)
CodeContracts是对的。在调用instance.bar = null
方法之前,没有什么能阻止您设置BarLength()
。
答案 1 :(得分:2)
您的代码包含私有静态初始化实例:
private static Foo instance = new Foo();
您是否认为这意味着实例构造函数将始终在访问任何静态方法之前运行,因此确保bar
已初始化?
在单线程的情况下,我认为你是对的。
事件的顺序是:
Foo.BarLength()
Foo
的静态初始化(如果尚未完成)instance
,其实例为Foo
Foo.BarLength()
然而,每个App Domain只会触发一次类的静态初始化 - 而且在调用任何其他静态方法之前,IIRC没有阻止它确保完成。
所以,你可以有这样的场景:
Foo.BarLength()
Foo
的静态初始化(如果尚未完成)启动Foo.BarLength()
Foo
的静态初始化,因为它已经在进行中Foo.BarLength()
null
静态成员instance
Contracts分析器无法知道您从不以多线程方式运行代码,因此必须谨慎行事。
答案 2 :(得分:2)
更新:问题似乎是invariants are not supported for static fields。
第二次更新:下面列出的方法是currently the recommended solution。
可能的解决方法是为instance
创建一个属性,Ensure
是您要保留的不变量。 (当然,你需要Assume
它们才能证明Ensure
。)一旦你完成了这个,你就可以使用该属性,并且所有不变量都应该被正确证明。
以下是使用此方法的示例:
class Foo
{
private static readonly Foo instance = new Foo();
private readonly string bar;
public static Foo Instance
// workaround for not being able to put invariants on static fields
{
get
{
Contract.Ensures(Contract.Result<Foo>() != null);
Contract.Ensures(Contract.Result<Foo>().bar != null);
Contract.Assume(instance.bar != null);
return instance;
}
}
public Foo()
{
Contract.Ensures(bar != null);
bar = "Hello world!";
}
public static int BarLength()
{
Contract.Assert(Instance != null);
Contract.Assert(Instance.bar != null);
// both of these are proven ok
return Instance.bar.Length;
}
}
答案 3 :(得分:0)
如果您将字段“bar”标记为只读,则应该满足静态分析器的要求,该字段在ctor执行后永远不会被设置为其他任何内容。
答案 4 :(得分:-1)
我同意你的看法。 instance
和bar
都是私有的,因此CodeContracts应该能够知道instance.bar
永远不会设置为null。