接口契约混淆静态检查器

时间:2013-02-19 16:24:04

标签: c# code-contracts

假设以下简单代码:

public class Foo // : IFoo
{
    private string _field;

    public string Property
    {
        get { return _field; }
    }

    private void SetField()
    {
        _field = " foo ";
    }

    private string Method()
    {
        SetField();
        return Property.Trim();
    }
}

静态检查器能够证明Property使用它时Method不会为空。

现在,我引入一个接口和一个契约,静态检查器开始抱怨:“可能在空引用上调用一个方法'this.Property'。

这是一个错误还是我错过了什么?


带有界面的代码如下所示:

public class Foo : IFoo
{
    private string _field;

    public string Property
    {
        get { return _field; }
    }

    private void SetField()
    {
        _field = " foo ";
    }

    private string Method()
    {
        SetField();
        return Property.Trim();
    }
}

[ContractClass(typeof(IFooContract))]
public interface IFoo
{
    string Property { get; }
}

[ContractClassFor(typeof(IFoo))]
public abstract class IFooContract : IFoo
{
    public string Property
    {
        get { throw new System.NotImplementedException(); }
    }
}

我的设置是这样的:

我得到以下输出:

[...]
C:\{path}\CC2.cs(11,19): message : CodeContracts: Suggested ensures: Contract.Ensures(Contract.Result<System.String>() == this._field);
C:\{path}\CC2.cs(16,13): message : CodeContracts: Suggested ensures: Contract.Ensures(this._field != null);
C:\{path}\CC2.cs(21,13): message : CodeContracts: Suggested ensures: Contract.Ensures(Contract.Result<System.String>() != null);
C:\{path}\CC2.cs(21,13): message : CodeContracts: Suggested ensures: Contract.Ensures(this._field != null);
C:\{path}\CC2.cs(21,13): message : CodeContracts: Suggested ensures: Contract.Ensures(this.Property.Trim() != null);
C:\{path}\CC2.cs(21,13): message : CodeContracts: Suggested ensures: Contract.Ensures(Contract.Result<System.String>() == this.Property.Trim());
[...]
C:\{path}\CC3.cs(33,13): warning : CodeContracts: Possibly calling a method on a null reference 'this.Property'
[...]

我使用Visual Studio 2010 Ultimate和.NET 4作为目标框架。

2 个答案:

答案 0 :(得分:1)

不是一个答案,而是对这个问题的一些想法。这不是那种混淆代码合同的接口合同。我设法用简单的例子重现了这个,没有ContractClass的接口。只需将第二个示例更改为简单

即可
//Foo's declaration

public interface IFoo
{
    string Property { get; }
}

你会得到同样的错误。即使在“属性”字段上添加Contract.Assume(_field != null);也无法修复它(它会修复此问题,将此Assume添加到SetField方法)。我没有设法用Invariants抑制空引用异常警告。唯一有用的是一个非常难看的解决方案,你必须为接口契约提供后置条件,并在属性字段中给代码契约提供assume的提示。完整代码如下所示

public class Foo  : IFoo
{
    private string _field;

    public string Property
    {
        get
        {
            Contract.Assume(_field != null);
            return _field;
        }
    }

    private void SetField()
    {
        _field = " foo ";

    }

    private string Method()
    {
        SetField();
        return Property.Trim();
    }
}

[ContractClass(typeof(IFooContract))]
public interface IFoo
{
    string Property { get; }
}

[ContractClassFor(typeof(IFoo))]
public abstract class IFooContract : IFoo
{
    public string Property
    {
        get
        {
            Contract.Ensures(Contract.Result<string>() != null);
            throw new NotImplementedException();
        }
    }
}

编辑: 由于_field可以为null,我建议使用此方法体为分析器提供提示,以便它不会打扰null引用警告。

private string Method()
{
    SetField();
    Contract.Assume(Property != null);
    return Property.Trim();
}

P.S。正如约翰·索梅兹在pluralsight training regarding Code contracts所说的那样“静态分析是一项复杂而神秘的任务,如果没有暗示分析员采用假设方法调用,就很难解决这个问题”。

答案 1 :(得分:0)

如果问题只出现在类中的代码中 - 就像我的例子中的情况那样 - 实用的解决方案很简单:

使用支持字段而不是属性:

public class Foo : IFoo
{
    private string _field;

    public string Property
    {
        get { return _field; }
    }

    private void SetField()
    {
        _field = " foo ";
    }

    private string Method()
    {
        SetField();
        return _field.Trim();
    }
}