9

Assume the following code:

[ContractClass(typeof(ICC4Contract))]
public interface ICC4
{
    bool IsFooSet { get; }
    string Foo { get; }
}

public class CC4 : ICC4
{
    private string _foo;

    public bool IsFooSet { get { return Foo != null; } }

    public string Foo { get { return _foo; } }
}

[ContractClassFor(typeof(ICC4))]
public abstract class ICC4Contract : ICC4
{
    public bool IsFooSet
    {
        get
        {
            Contract.Ensures((Contract.Result<bool>() && Foo != null)
                             || !Contract.Result<bool>());
            return false;
        }
    }

    public string Foo
    {
        get
        {
            Contract.Ensures((Contract.Result<string>() != null && IsFooSet)
                             || !IsFooSet);
            return null;
        }
    }
}

The contracts try to say:

  1. IsFooSet will return true if Foo is not null.
  2. Foo doesn't return null if IsFooSet returns true.

This almost works.
However, I get an "ensures unproven" on return _foo;, because the checker doesn't realize that Foo will always equal _foo.

Changing Foo to an automatic property with a private setter removes that warning, but I don't want to do that (I don't like automatic properties with private setters).

What do I have to change in the above code to make the warning go away while preserving the _foo field?

The following doesn't work:

  1. Changing IsFooSet to use _foo instead of Foo. It will result in an additional "ensures unproven" on IsFooSet.
  2. Adding an invariant Foo == _foo. This will result in an "invariant unproven" on the implicit, default constructor. Furthermore, on a real code-base the processing time of the static checker will be magnitudes higher.
  3. Adding Contract.Ensures(Contract.Result<string>() == _foo); to the getter of Foo as per this answer doesn't change anything.
Community
  • 1
  • 1
Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443
  • 1
    This won't help with your issue, but may I ask you why you don't like automatic properties with private setters? – ken2k Feb 26 '13 at 13:32
  • 1
    Most of the time, those properties are invariants of a class, i.e. the backing field should be made readonly. That's not possible with an automatic property. I actually try to completely avoid automatic properties. Reasons are outlined [here](http://blog.ploeh.dk/2011/05/26/CodeSmellAutomaticProperty.aspx). – Daniel Hilgarth Feb 26 '13 at 13:35
  • Would it be removed if you set `_foo` to an empty string by default? It feels like the problem is more that the default value of `_foo` is going to be `null`. – Mike Perrenoud Feb 26 '13 at 13:35
  • @MichaelPerrenoud: No, that doesn't change anything. Why should it? The default value of `_foo` has no impact on the defined contracts. – Daniel Hilgarth Feb 26 '13 at 13:36
  • @DanielHilgarth, I was working off the assumption that the only reality the compiler would have is the default value of `_foo` which breaks the contract. – Mike Perrenoud Feb 26 '13 at 13:38
  • @MichaelPerrenoud: That assumption isn't correct. Furthermore, `_foo` being `null` doesn't actually break the contract. – Daniel Hilgarth Feb 26 '13 at 13:42
  • I totally agree with Daniel wrt private properties. `readonly` properties ensure that you don't change them outside the constructor; when you're reading the code, you can be sure that the fields won't be changing (even though the fields themselves might be mutable). – Matthew Watson Feb 26 '13 at 14:01

1 Answers1

2

You can use short-circuiting to simplify the condition, and that works for some reason:

[ContractClassFor(typeof(ICC4))]
public abstract class ICC4Contract : ICC4
{
    public bool IsFooSet
    {
        get
        {
            Contract.Ensures(!Contract.Result<bool>() || Foo != null);
            return false;
        }
    }

    public string Foo
    {
        get
        {
            Contract.Ensures(!IsFooSet || Contract.Result<string>() != null);
            return null;
        }
    }
}
Eli Arbel
  • 22,391
  • 3
  • 45
  • 71