13

Is there any way to put contracts on automatically implemented properties in .NET? (And how if the answer is 'Yes')?

(I assume using .NET code contracts from DevLabs)

jgauffin
  • 99,844
  • 45
  • 235
  • 372
wh1t3cat1k
  • 3,146
  • 6
  • 32
  • 38
  • 2
    Have you tried using class invarients for this purpose? I suspect that would work, unless the invarients are not checked when returning from an auto-prop setter. (I am not yet running a version of VS with code contracts, so I can't test this myself. I don't know if I will ever get a version with the static checker. :-( ) – Jeffrey L Whitledge Apr 02 '11 at 23:11
  • I'm not quite sure if the invariants will be checked in the Release version of the project, because for this configuration I've turned on the option "Only public surface contracts", which I suspect to deal only with Contract.Requires in public methods. I'd be happy if I'm wrong. – wh1t3cat1k Apr 03 '11 at 09:44
  • But I'm afraid I'm right: "...You can add invariant methods to enforce data integrity (Section 2.3) ... These checks are enforced in your testing builds with runtime checking enabled, but disappear from your shipping code." – wh1t3cat1k Apr 03 '11 at 09:48
  • I believe that Invariants on auto-implemented properties actually add the Requires and Ensures. Need to double-check this though... – porges Apr 04 '11 at 04:05
  • I've made a custom check with checking option "ReleaseRequires" - which is surely to be used in the Release configuration - and the invariant wasn't checked at all, including the requirements related to the auto-property. No matter if the flag "public surface contracts only" was checked or not. – wh1t3cat1k Apr 05 '11 at 09:08

3 Answers3

16

Yes, this is possible - all that is needed is to add your contract condition to the [ContractInvariantMethod] method in your class, which then adds the equivalent Requires precondition to the automatic setter, and a post condition Ensures is added to the get. From section 2.3.1 of the Reference

As the example illustrates, invariants on auto-properties turn into:

  1. A precondition for the setter
  2. A postcondition for the getter
  3. An invariant for the underlying backing field

And by example:

public int MyProperty { get; private set ;}

[ContractInvariantMethod]
private void ObjectInvariant ()
{
  Contract.Invariant ( this.MyProperty >= 0 );
}

"Is equivalent to the following code:"

private int backingFieldForMyProperty;
public int MyProperty 
{
  get 
  {
    Contract.Ensures(Contract.Result<int>() >= 0);
    return this.backingFieldForMyProperty;
  }

  private set 
  {
    Contract.Requires(value >= 0);
    this.backingFieldForMyProperty = value;
  }
}

[ContractInvariantMethod]
private void ObjectInvariant () 
{
  Contract.Invariant ( this.backingFieldForMyProperty >= 0 );
...
StuartLC
  • 104,537
  • 17
  • 209
  • 285
1

I'm thinking not, but you could easily write a snippet that would do this. If you go this route, here is a free snippet editor that will make the task very easy.

BrandonZeider
  • 8,014
  • 2
  • 23
  • 20
1

Thanks Porges.

My mistake was that I actually used ReleaseRequires option, which, indeed, deals only with generic version of the method, Requires<T>.

Invariant which is put on an auto-implemented property is really turned into a Requires precondition, but it's not generic - that's why it didn't work using this option.

What to do:

  • VARIANT 1. Consider using code snippets and lovely Requires<T> instead of auto-implemented properties - that enables us to use exceptions of desired type.

  • VARIANT 2. Change the option ReleaseRequires to Preconditions in the Code Contracts' options and feel free to write invariants on auto-properties - the rewriter tool will automatically change them into the Requires. However, they will be non-generic - that means, in case of contract broken, a ContractException will be thrown and there is no way to change this behaviour.

Thanks everyone for the help!

wh1t3cat1k
  • 3,146
  • 6
  • 32
  • 38