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静的チェッカーは、使用時に null ではないことを証明できますMethod

ここで、コントラクトと共にインターフェイスを導入すると、静的チェッカーが「null 参照 '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

Not quite an answer, but some thoughts on the question. This is not that interface contract, which confuse Code Contracts. I've managed to reproduce this with simple example without ContractClass for interface. Just change second example to simple

//Foo's declaration

public interface IFoo
{
    string Property { get; }
}

And you will get the same error. Even adding Contract.Assume(_field != null); on Property field doesn't fix it (it will fix it adding this Assume to SetField method). I didn't manage to suppress null reference exception warning with Invariants either. The only thing that worked is quite an ugly solution, where you have to provide postcondition for interface contract and give Code Contract's a hint with assume in Property field. Full code is shown below

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();
        }
    }
}

Edit: As _field can be null, I suggest to use this method body to give hints to the analyser, so that it won't bother with null reference warning.

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

p.s. as John Sonmez says at pluralsight training regarding Code contracts "Static analysis is a complex, mysterious task, which hardly can be worked out without hints to the analyser suing Assume method calls".

于 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 に答える