Can I leave contracts in code that I'm merging

2019-02-16 09:41发布

问题:

For the last few months I've been developing a side project for my company, but the higher-ups have now decided it would be a good fit in an existing product.

I've been developing the side project using Microsoft's Code Contracts for static type checking (partly because I hadn't used them before and was eager to learn).

My problem is that if I check in my code to the code base with Contracts in place, will every other developer need the Code Contracts tools installed to be able to continue developing? I know for a fact that none of them have it installed, and I'm the junior here so I doubt I could convince them all to take it up.

I'm using .Net 4.5, so the Code Contract libraries are included, but I'm wondering if Visual Studio will complain that they're not building with CONTRACTS_FULL specified in the build options every time they go to build, or, if I leave CONTRACTS_FULL in the build options, what will happen when another developer tries to build? Additionally I'm wondering how the end-product will act when a Contract fails, but the code has not been built with the Code Contracts Rewriter.

I created a new solution with just one project. Created a simple function that fired a code contract violation, with code contracts uninstalled and CONTRACTS_FULL not specified. Built and ran it and received the following error:

Run-time exception (line 8): An assembly (probably "hdxticim") must be rewritten using the code contracts binary rewriter (CCRewrite) because it is calling Contract.Requires<TException> and the CONTRACTS_FULL symbol is defined.  Remove any explicit definitions of the CONTRACTS_FULL symbol from your project and rebuild.  CCRewrite can be downloaded from http://go.microsoft.com/fwlink/?LinkID=169180. 
After the rewriter is installed, it can be enabled in Visual Studio from the project's Properties page on the Code Contracts pane.  Ensure that "Perform Runtime Contract Checking" is enabled, which will define CONTRACTS_FULL

I think the error message needs rewriting, as CONTRACTS_FULL is most definitely not specified.

Thanks to Matías Fidemraizer, we've worked out that this happens when using Contract.Requires<TException>() and not on Contract.Requires().

Ideally, I'd like to modify this behaviour so that the Contract fires the provided exception as if it were a normal guard statement instead of complaining about the rewriter.

Here's a fiddle demonstrating the problem: https://dotnetfiddle.net/cxrAPe

回答1:

The short answer is: Yes. If you check in the code with Code Contracts, then all developers that might build that code must also have Code Contracts installed in order to build the code.

In contradiction to what @CBauer wrote in his answer, there is a "blessed" package for Code Contracts. No, it's not a NuGet package—it's an MSI installer based installation.

Finally, if you are in a Continuous Integration environment for debug builds (e.g. Development, QA/QC and/or Testing), then those build servers will also need to have Code Contracts installed.

When you use Code Contracts, Debug builds always require the use of Code Contracts. Please note that this is not necessarily the case for Release builds. It depends on what form of contract checking you're using and the options specified in the Project Properties.

The Code Contracts manual has all the details. It's quite good and I highly recommend taking the time to read and understand it.

It should be noted that if you use the Contract.Requires<TException>(bool condition) form of preconditions, you must have Code Contracts enabled for Release builds (see Section 5: Usage Guidelines, specifically, page 20, Usage 2 scenario).

Since you are integrating this code into an existing codebase that has not been developed with Code Contracts, you should consider revising your Code Contracts Project Property settings to conform to Usage Scenario 3 outlined on page 20 of the Code Contracts manual, and reformulate your contracts using the "legacy" if-then-throw pattern. This will enable your team to best tranisition the code base to using Code Contracts everywhere, allowing you to eventually replace "legacy" if-then-throw precondition checks with actual Code Contracts Contract.Requires(bool condition) checks, and if you like, Contract.Requires<TException>(bool condition) checks.

UPDATE: There Will Soon Be A NuGet Package For Code Contracts I was at the new GitHub repository for Code Contracts today. For those of you who don't know, Microsoft has open-sourced this and it is now a community-driven effort.

They recently (back in January) announced v1.10.xxxx.RC1 release. You can find information about it here on their GitHub repository.



回答2:

Unfortunately it's difficult to keep a library that is not installed via nuget up to date without forcing your coworkers to maintain their personal environment. Code contracts does not appear to have an official Microsoft-blessed package out there for use.

fourpastmidnight's post below has a recently updated answer to my original concern above, but the bottom half I think remains relevant even now. Get your buy-in, people!

In my (admittedly subjective) experience, tools like this come to be reviled if buy-in is not established among coworkers in advance. You might try broaching the subject slowly to see if they would be receptive to using it.



回答3:

Contract.Requires<TException>() will fail if the code contracts rewriter is not installed, regardless of setting CONTRACTS_FULL or not.

One solution is to build a separate class FailableContract which tests the preprocessor condition CONTRACTS_FULL, which is set by Code Contracts.

Then all you have to do is remember to remove CONTRACTS_FULL from your build parameters before committing your code to the code base. You still get code contracts, everybody else gets guard statements.

A small portion of FailableContract (which I'm still writing):

public class FailableContract
{

    public static void Requires(bool condition)
    {
        #if CONTRACTS_FULL
            Contract.Requires(condition);
        #else
            if (!condition) throw new ArgumentException(); //Just go with an argument exception because we haven't been supplied with an exception type.
        #endif
    }

    public static void Requires<TException>(bool condition) where TException : Exception, new()
    {
        #if CONTRACTS_FULL
            Contract.Requires<TException>(condition);
        #else
            if (!condition) throw new TException();
        #endif
    }
}

Then a simple Find, Replace Contract. to FailableContract.should get most issues.