Skip to content

Pathoschild/DesignByContract

Repository files navigation

Pathoschild.DesignByContract is a little aspect-oriented library which enables design-by-contract preconditions and postconditions. By annotating your code with attributes like [NotNull], you can remove common validation code and provide helpful exception messages, making your code more robust and refactor-safe. These annotation attributes are also recognized by ReSharper when it has an equivalent annotation, which gives you real-time feedback on contract violations as you type.

The library uses PostSharp for its compile-time aspect weaving, which requires a free license to build the annotated project. (A license is only required to build the project containing the annotations — there are no licensing requirements to reference the compiled project.)

##Usage You can define a contract on code using annotation attributes like [NotNull] on return values, method arguments, and properties. Annotations on interfaces are inherited by their implementations. You enable contract validation by applying [DesignedByContract] to the class (or unit of code):

###Example The following method with the contract annotations:

    [DesignedByContract]
    public class Sword
    {
        [return: NotNull, NotBlank]
        public string Hit([NotNull] string actor, [NotNull] string target)
        {
            return String.Format("{0} hit {1} with a sword!", actor, target);
        }
    }

is equivalent to this one without:

    public class Sword
    {
        public string Hit(string actor, string target)
        {
            if (actor == null) throw new ArgumentNullException("actor", "The value cannot be null for parameter 'actor' of method 'Sword::Hit'.");
            if (target == null) throw new ArgumentNullException("target", "The value cannot be null for parameter 'target' of method 'Sword::Hit'.");

            string value = String.Format("{0} hit {1} with a sword!", actor, target);

            if(value == null) throw new NullReferenceException("The return value cannot be null for method 'Sword::Hit'.");
            if(String.IsNullOrWhiteSpace(value)) throw new InvalidOperationException("The return value cannot be blank or consist entirely of whitespace for method 'Sword::Hit'.");

            return value;
        }
    }

###Available annotations The following annotations are implemented out of the box. When a contract is violated, an annotation will by convention throw a descriptive message with the type ParameterContractException (which subclasses ArgumentException) or ReturnValueContractException (which subclasses InvalidOperationException).

  • [NotNull] marks a value that cannot be null.
  • [NotBlank] marks a string value that cannot be empty or only whitespace.
  • [NotEmpty] marks a sequence value that cannot have zero elements.
  • [NotDefault] marks a value that cannot be equal to the default value for its type (e.g., null for a reference type or 0 for an integer).
  • [HasType] marks a value that must implement one of the specified types (with possible inheritance). This is intended to make code that must unbox arguments more robust.

###Creating annotations You can create new annotations by implementing attributes with any of the following interfaces (see an example annotation):

###Installation You only need to do the following for uncompiled projects containing annotations. You can reference their compiled DLLs without knowing about annotations or PostSharp, so using these annotations shouldn't affect redistribution. (These steps assume you're using Visual Studio.)

  1. Acquire a free Starter License for PostSharp.
  2. Install the Pathoschild.DesignByContract NuGet package for the projects that will contain annotations.

And that's it; you can now enforce code contracts throughout your code.

##Performance ###Runtime Efficient runtime performance is a core design goal. The [DesignedByContract] aspect analyzes contracts at compile time and injects non-reflection validation logic into the method, so it's very fast at runtime.

For example, this is the decompiled source code generated by the Sword example above:

        [return: NotNull, NotBlank]
        public string Hit([NotNull] string actor, [NotNull] string target)
        {
          MethodExecutionArgs args = new MethodExecutionArgs(null, new Arguments<string, string>() { Arg0 = actor, Arg1 = target });
          Sword.<>z__Aspects.a1.OnEntry(args);
          string str = string.Format("{0} hit {1} with a sword!", actor, target);
          args.ReturnValue = str;
          Sword.<>z__Aspects.a1.OnSuccess(args);
          return str;
        }

###Build-time The library analyses contracts at build time, so large projects or solutions with many projects will take longer to build.

##Partial trust compatibility This library requires full trust as of 1.2; you can use an earlier version with the ENABLE_PARTIAL_TRUST build constant in partial-trust environments. This may increase build time.

##Future to-do

About

A high-performance aspect-oriented argument validation framework built on top of the free edition of PostSharp.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages