More Newbie Code Contract Questions

Answered More Newbie Code Contract Questions

  • Saturday, November 06, 2010 10:23 PM
     
      Has Code

    Hi, am trying to fully understand the use of Code Contracts. Lets take a really simple example:

    public static string DoSomething(object anObject)
            {
                Contract.Requires(anObject != null);
                return anObject.ToString();
            }

    1.  What is the contract supposed to be protecting from:  Design Time errors, or runtime errors?  To me, a design time error would be a call to DoSomething(null) in code that could be caught by the static checker.  But what about runtime errors such as the data being passed to DoSomething() from data in a database.  This is going to be caught only at runtime by the rewriter.

    2.  Should I be wrapping calls to DoSomething() in a try - catch to catch the runtime errors?  Or it seems to me that I should check the data from the database for proper values before calling it.  Meaning don't do a try catch at all, because it will either be caught by the static checker, or be caught by the value checking immediately after getting the values.

    3.  In user interface development, one of the most frequent sources of bugs comes from a control not returning an expected value.  For example, a dropdown box returns null unexpectedly.  It seems this is another case were those such values should be validated before doing anything with them.

    4.  It seems that wrapping a call to DoSomething() in a try catch is sort of useless, as it appears that you can't catch a ContractViolationException except by catching Exception.  

    5.  It seems that turning off Runtime Contract enforcement is recommended.  But this then eliminates all the benfits of parameter checking in the first place as Brad Abrams talked about so many times.

    6.  Often I will have several layers, where one layer is just passing up a parameter to a higher layer.  Should I be doing Code Contracts at each layer?  Or only the first, or only the last?

    Anyhow, just trying to get up to speed here.

    Greg

All Replies

  • Sunday, November 07, 2010 12:03 AM
     
     Answered

    Hi Greg,

    1. What is the contract supposed to be protecting from:  Design Time errors, or runtime errors?  [snip]

      Both.  As mentioned, the static checker will help you to verify that all callers satisfy the method's contracts.  If these warnings are disregarded, or if the static checker is not being used, then an exception is thrown at runtime when a caller does not satisfy the method's contracts.

       

    2. Should I be wrapping calls to DoSomething() in a try - catch to catch the runtime errors?  [snip]

      No, do not catch contract exceptions at all.  A contract exception indicates a bug in your code that in most cases cannot be handled gracefully at runtime.  The best thing to do when a contract exception is thrown is to allow the application to crash, identify the cause of the bug and fix it.

      For applications that have already been deployed:

      In most applications it makes sense to have a global exception handling routine (e.g., AppDomain.UnhandledException) that simply logs all exceptions and allows the program to fail.  This is especially useful for exceptions thrown on background threads since they will surely cause the application to stop due to the default exception handling policy of the CLR.

      In some applications, it makes sense to have a global exception handling routine for the UI thread (e.g., Dispatcher.UnhandledException) that logs all exceptions and allows the program to continue from a safe, reset state.  If there's no way to reset the application while keeping it alive, being absolutely sure that no user data can be corrupted in any way, then simply fail fast instead.

      There are absolutely zero cases where it's safe to catch specific contract exception types in a global exception handler.  (In most cases, you should not catch any specific exception types in a global exception handler, contract-related or not.)

       

    3. In user interface development, one of the most frequent sources of bugs comes from a control not returning an expected value. [snip]

      Contract.Assume can be useful in these cases.  Although, if the static checker can actually prove the expected value somehow, then use Contract.Assert instead.

       

    4. It seems that wrapping a call to DoSomething() in a try catch is sort of useless [snip]

      See my response to #2.

       

    5. It seems that turning off Runtime Contract enforcement is recommended.  But this then eliminates all the benfits of parameter checking in the first place as Brad Abrams talked about so many times.

      I believe that the recommendation is to disable runtime contract verification only for those applications that may suffer noticeable performance degradation or bloat with the addition of the rewriter's code.

      Even for applications that really have no need for runtime contract verification (theoretically), such as an application that is fully checked by the static checker and has absolutely no unchecked external callers, it's probably a good idea to at least keep pre- and post-condition verification enabled if it doesn't have any noticeable negative impact.  This is especially true for debug builds, where the debug assertion dialog is extremely valuable for debugging contract runtime failures, but it's also true for production builds, where contract violations that may have slipped through the cracks can reveal themselves in awkward, hard-to-debug places (without contract exceptions enabled, that is).

       

    6. Often I will have several layers, where one layer is just passing up a parameter to a higher layer.  Should I be doing Code Contracts at each layer?  Or only the first, or only the last?

      Every layer should have appropriate contracts.  Code contracts push requirements to callers, up to the caller that actually creates the data and must satisfy the contracts of its immediate callee.

      When a caller passes an argument to a method that has a contract on it, that caller is responsible for satisfying the contract for that argument.  If the caller is passing one of its own parameters as the argument, then it must define the same contract on itself so that its callers will know what contracts they must satisfy in order to successfully call the method.  Eventually, some caller will be responsible for creating the argument and passing it to the top method on the call stack.  This caller must satisfy the requirements of the bottom-most method in the call stack, and the only way for it to know what those contracts are is if all of the methods in the call stack bubble up the contracts by specifying them as their own contracts.

       

    - Dave


    http://davesexton.com/blog
    • Marked As Answer by Greg Gum Sunday, November 07, 2010 1:38 PM
    •