CodeContracts:可能在空引用上调用方法

时间:2010-04-20 23:27:02

标签: c# visual-studio-2010 .net-4.0 code-contracts

我正在与CodeContracts static analysis tool进行争论。

我的代码:

Screenshot http://i40.tinypic.com/r91zq9.png

ASCII version

该工具告诉我instance.bar可能是空引用。我相信相反。

谁是对的?我怎么能证明它错了?

5 个答案:

答案 0 :(得分:2)

CodeContracts是对的。在调用instance.bar = null方法之前,没有什么能阻止您设置BarLength()

答案 1 :(得分:2)

您的代码包含私有静态初始化实例:

private static Foo instance = new Foo();

您是否认为这意味着实例构造函数将始终在访问任何静态方法之前运行,因此确保bar已初始化?

在单线程的情况下,我认为你是对的。

事件的顺序是:

  1. 致电Foo.BarLength()
  2. Foo的静态初始化(如果尚未完成)
  3. 静态初始化私有静态成员instance,其实例为Foo
  4. 输入Foo.BarLength()
  5. 然而,每个App Domain只会触发一次类的静态初始化 - 而且在调用任何其他静态方法之前,IIRC没有阻止它确保完成

    所以,你可以有这样的场景:

    1. 线程Alpha:致电Foo.BarLength()
    2. 线程Alpha:类Foo的静态初始化(如果尚未完成)启动
    3. 上下文切换
    4. 主题测试版:致电Foo.BarLength()
    5. 线程测试版:无调用到类Foo的静态初始化,因为它已经在进行中
    6. 主题测试版:输入Foo.BarLength()
    7. 主题测试版:访问null静态成员instance
    8. 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)

我同意你的看法。 instancebar都是私有的,因此CodeContracts应该能够知道instance.bar永远不会设置为null。