Validating parameters properties with Code Contrac

2019-08-09 00:16发布

问题:

I had a discussion with a colleague regarding the usage of Code Contracts to perform prerequisites checks.

Let's say we have the following code:

namespace Project
{
    using System;
    using System.Diagnostics.Contracts;

    public class Thing
    {
        public string Foo { get; set; }

        public int Bar { get; set; }
    }

    public class ThingsManipulator
    {
        public void AddThing(Thing thing)
        {
            Contract.Requires<ArgumentNullException>(thing != null);

            // Do something
        }
    }
}

If in the // Do something I'm accessing thing.Foo and thing.Bar to do things, should I validate them too through Code Contracts?

public void AddThing(Thing thing)
{
    Contract.Requires<ArgumentNullException>(thing != null);
    Contract.Requires<ArgumentException>(!string.IsNullOrWhiteSpace(thing.Foo));
    Contract.Requires<ArgumentException>(thing.Bar > 0);

    // Do something
}

My colleague says that only the parameter as a whole should be checked (i.e. we should only place the first contract), I think methods should check what they use regardless if it's the whole parameter or one of its properties (i.e. we should all the three contracts).

Please note, I do understand and agree with that if the properties of a parameter should always fulfill a requirement, that requirement should be placed in the object's invariant checks.

What I'm referring to are values which are generally valid, but not valid for a particular method (e.g. in the example above thing.Bar may happily hold negative values, but AddThing does not like them).

My colleague says in these cases the method signature should explicit all the items it's using instead of a single object (e.g. AddThing(string thingFoo, int thingBar)), and run the checks on them.

So:

  • Should we validate what a method uses or only the parameters as a whole and "explode" the parameters?
  • Is there a technical reason to do it (regardless of whatever "it" means) or is it a matter of preference?

I haven't been able to find guidelines about this in the manual, maybe I missed something?

回答1:

It helps to think of contracts not as validating things, so much as being statements of fact, which the verifier attempts to prove. You're in essence saying "as long as my inputs are correct (Contract.Requires), then the following properties hold (Contract.Ensures, Contract.Assert, Contract.Invariant)".

Contracts should be statements that should always be true about the context they're used in. In your example, you're saying that a Thing's Foo is required to be non-empty only when they're being used by a ThingsManipulator.

If you could say that a Thing's Foo is always required to be non-empty then that's always true about a Thing and the contracts belong on Thing.

In this case you can't, so I think this becomes an OO design problem about the parameters. Thinking aloud:

  1. Methods should have as few parameters as possible (see https://stackoverflow.com/a/175035/1554471).
  2. AddThing being a public method, it should probably be dealing with higher-level abstractions, not details like Framework value types.
  3. AddThing is currently the only method that has these restrictions on Thing.
  4. So keep a single parameter of type Thing, and check its properties.

If (3) stopped being true, then it might be worth creating a new type that decorates or otherwise derives from Thing, and putting the contracts on there, to avoid the problem of repeating yourself. But at the moment, YAGNI.