我正在与CodeContracts静态分析工具争论。
我的代码: 这段文字的翻译如下:
我的代码: 这段文字的翻译如下:
(ASCII 版本)
这个工具告诉我instance.bar
可能是一个空引用。我相信相反。
谁是对的?我怎样才能证明它是错的?
(ASCII 版本)
这个工具告诉我instance.bar
可能是一个空引用。我相信相反。
谁是对的?我怎样才能证明它是错的?
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;
}
}
bar
是实例字段,不是静态的!?无论如何,我已经将勾选标记移到了这个答案,因为这是目前最好的解释。 - dtbinstance
是静态的,因此通过它访问的任何字段也无法正常工作。实际上,我已经收到了团队中一位成员的回复,似乎我想出的解决方案是目前针对静态字段的推荐解决方案。 - porgesCodeContracts是正确的。在调用BarLength()
方法之前,没有任何阻止您设置instance.bar = null
。
instance.bar = null
,而且 instance.bar
是私有的,所以它不可能为空,对吧? - dtbinstance.bar
永远不会是null
?将bar
设置为只读并在构造函数中添加Contract.Ensures(bar != null);
并不能解决问题。在BarLength()
中使用Contract.Assume(instance.bar != null);
可以解决问题,但看起来很丑。 - dtb你的代码包含一个私有静态初始化实例:
private static Foo instance = new Foo();
bar
?Foo.BarLength()
2.初始化类Foo
的静态部分(如果尚未完成)Foo
的实例初始化私有静态成员instance
4.进入Foo.BarLength()
然而,类的静态初始化仅在每个应用程序域中触发一次,并且我IRC没有阻塞以确保在调用任何其他静态方法之前完成。Foo.BarLength()
2.线程Alpha:类Foo
的静态初始化(如果尚未完成)开始Foo.BarLength()
5.线程Beta:不需要调用类Foo
的静态初始化,因为那已经在进行中Foo.BarLength()
7.线程Beta:访问null
静态成员instance
合同分析器无法知道您永远不会以多线程方式运行代码,因此必须谨慎处理。instance
将为 null,而不是 instance.bar
。instance.bar
只有在 Foo 实例的构造函数中分配之前才为 null,但实例仅在构造函数完成后严格存储在字段中。 - dtbbar
标记为只读没有任何效果。由于某种原因,分析器似乎没有意识到我在构造函数中将bar
赋值给一个非空值。 - dtb我同意你的观点。instance
和bar
都是私有的,所以CodeContracts应该能够知道instance.bar
永远不会被设置为null。