3

假设以下简单代码:

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'
[...]

我正在使用带有 .NET 4 的 Visual Studio 2010 Ultimate 作为目标框架。

4

2 回答 2

1

不是一个完整的答案,而是对这个问题的一些想法。这不是混淆代码契约的接口契约。我已经设法用没有ContractClass接口的简单示例重现了这一点。只需将第二个示例更改为简单

//Foo's declaration

public interface IFoo
{
    string Property { get; }
}

你会得到同样的错误。即使添加Contract.Assume(_field != null);属性字段也不能修复它(它将修复它添加AssumeSetField方法中)。我也没有设法用不变量来抑制空引用异常警告。唯一有效的是一个非常丑陋的解决方案,您必须为接口合同提供后置条件,并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可以为空,我建议使用此方法体向分析器提供提示,这样它就不会受到空引用警告的困扰。

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

ps 正如 John Sonmez 在有关代码合同的复数视力培训中所说的那样, “静态分析是一项复杂而神秘的任务,如果没有提示分析器起诉 Assume 方法调用,几乎无法解决它”。

于 2013-02-19T17:24:14.443 回答
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();
    }
}
于 2013-02-21T12:23:19.393 回答