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

发布于 2024-08-30 07:47:04 字数 543 浏览 3 评论 0原文

我与 CodeContracts 静态分析工具发生争执。

我的代码:

“截图"

(ASCII 版本)

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

谁是对的?我如何证明它是错误的?

I'm having an argument with the CodeContracts static analysis tool.

My code:

Screenshot

(ASCII version)

The tool tells me that instance.bar may be a null reference. I believe the opposite.

Who is right? How can I prove it wrong?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

蓬勃野心 2024-09-06 07:47:04

更新:问题似乎是静态字段不支持不变量

第二次更新:下面概述的方法是目前推荐的解决方案

一种可能的解决方法是为实例创建一个属性,确保是您想要保留的不变量。 (当然,您需要假设它们才能证明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;
    }
}

Update: It seems the problem is that invariants are not supported for static fields.

2nd Update: The method outlined below is currently the recommended solution.

A possible workaround is to create a property for instance that Ensures the invariants that you want to hold. (Of course, you need to Assume them for the Ensure to be proven.) Once you have done this, you can just use the property and all the invariants should be proven correctly.

Here's your example using this method:

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;
    }
}
英雄似剑 2024-09-06 07:47:04

代码合同是对的。在调用 BarLength() 方法之前,没有什么可以阻止您设置 instance.bar = null

CodeContracts is right. There is nothing stopping you from setting instance.bar = null prior to calling the BarLength() method.

走过海棠暮 2024-09-06 07:47:04

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

private static Foo instance = new Foo();

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

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

事件顺序为:

  1. 调用 Foo.BarLength()
  2. Foo 的静态初始化(如果尚未完成)
  3. 私有静态成员 instance< 的静态初始化/code> 与 Foo 实例
  4. 进入 Foo.BarLength()

但是,每个应用程序域仅触发类的静态初始化一次 - 并且 IIRC 不会阻塞确保在调用任何其他静态方法之前它已完成。

因此,您可能会遇到以下情况:

  1. Thread Alpha:调用 Foo.BarLength()
  2. Thread Alpha:类 Foo 的静态初始化(如果尚未完成)开始
  3. 上下文切换
  4. 线程 Beta:调用 Foo.BarLength()
  5. 线程 Beta:不调用Foo 的静态初始化,因为那是已经进行
  6. 线程 Beta:进入 Foo.BarLength()
  7. 线程 Beta:访问 null 静态成员 instance

合约分析器无法知道您永远不会以多线程方式运行代码,因此必须谨慎行事。

Your code includes a private static initialized instance:

private static Foo instance = new Foo();

Are you assuming that this means the instance constructor will always have run before access to any static method, therefore ensuring bar has been initialized?

In the single threaded case, I think you're right.

The sequence of events would be:

  1. Call to Foo.BarLength()
  2. Static initialization of class Foo (if not already completed)
  3. Static initialization of private static member instance with instance of Foo
  4. Entry toFoo.BarLength()

However, static initialization of a class is only ever triggered once per App Domain - and IIRC there's no blocking to ensure it's completed before any other static methods are called.

So, you could have this scenario:

  1. Thread Alpha: Call to Foo.BarLength()
  2. Thread Alpha: Static initialization of class Foo (if not already completed) starts
  3. Context Switch
  4. Thread Beta: Call to Foo.BarLength()
  5. Thread Beta: No Call to static initialization of class Foo because that's already underway
  6. Thread Beta: Entry to Foo.BarLength()
  7. Thread Beta: Access to null static member instance

There's no way the Contracts analyser can know that you'd never run the code in a multithreaded way, so it has to err on the side of caution.

盛夏尉蓝 2024-09-06 07:47:04

如果将字段“bar”标记为只读,则应该满足静态分析器的要求,即在构造函数执行后该字段永远不会被设置为其他任何内容。

If you mark the field 'bar' as readonly, that should satisfy the static analyzer that the field will never be set to anything else after the ctor executes.

千秋岁 2024-09-06 07:47:04

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

I agree with you. instance and bar are both private, so CodeContracts should be able to know instance.bar is never set to null.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文