This project has moved and is read-only. For the latest updates, please go here.

Extending CuttingEdge.Conditions

While CuttingEdge.Conditions covers most basic validation needs, developers will always have specific needs that CuttingEdge.Conditions (or any other library) doesn't cover. Though it is still possible to write those old fashion 'if (b) throw ex' statements, a more convenient solution is to write your own validation methods. This page gives some practical examples that show how to extend CuttingEdge.Conditions.

Before you start extending CuttingEdge.Conditions, there are two things you should pay attention to:

1. Place your extension class in your companies root namespace.
This way you'll never have to include that namespace. The only namespace you'll have to add is the CuttingEdge.Conditions namespace. After including this namespace, your own extension methods will show up automatically in the IntelliSense dropdown list.

2. Some members won't show up using IntelliSense.
Some members of the ConditionValidator<T> class are decorated with the EditorBrowsableAttribute, preventing them from showing up during IntelliSense. The most important hidden members are the ArgumentName and Value fields and the ThrowException method. Hiding those members makes a lot of sense, because they aren't used directly by users of the library. Showing them would clutter the API and make using the library more difficult. Hiding them however, makes extending the library harder and it is something you'll have to deal with while extending the library.

Adding new validations

The following code sample shows how to add an extra validation extension method, that you than can use just like the build-in validations:

C# example
using System.Collections.Generic;
using System.Linq;

using CuttingEdge.Conditions;

namespace YourCompanyRootNamespace
{
    public static class ConditionExtensions
    {
        public static ConditionValidator<T> ExistsIn<T>(
            this ConditionValidator<T> validator, IEnumerable<T> collection)
        {
            // Value, ArgumentName and ThrowException won't show up in the 
            // IntelliSense dropdown list, but you can use them.
            if (collection == null || !collection.Contains(validator.Value))
            {
                validator.ThrowException(validator.ArgumentName +
                    " should be in the supplied collection");
            }

            return validator;
        }
    }
}
VB.NET example
' TODO

The new 'ExistsIn' validation method can be used as follows:

C# example
void Method(int number)
{
    Condition.Requires(number, "number")
        .ExistsIn(new[] { 1, 2, 9, 10 });
}
VB.NET example
' TODO

Throwing different types of exceptions

Currently CuttingEdge.Conditions only supports validating preconditions using the Requires method overloads, and validating postconditions using the Ensures method overloads. A failing 'requires' results in an ArgumentException (or one of it's subtypes). A failing 'ensures' results in a PostconditionException. Throwing other types of exceptions is possible by extending CuttingEdge.Conditions.

The following example defines an 'Invariant' entry point method, that results in an InvalidOperationException on failure.

C# example
using System;
using CuttingEdge.Conditions;

namespace MyCompanyRootNamespace
{
    public static class Invariants
    {
        public static ConditionValidator<T> Invariant<T>(T value)
        {
            return new InvariantValidator<T>("value", value);
        }

        public static ConditionValidator<T> Invariant<T>(T value, 
            string argumentName)
        {
            return new InvariantValidator<T>(argumentName, value);
        }

        // Internal class that inherits from ConditionValidator<T>
        sealed class InvariantValidator<T> : ConditionValidator<T>
        {
            public InvariantValidator(string argumentName, T value)
                : base(argumentName, value)
            {
            }

            protected override void ThrowExceptionCore(string condition,
                string additionalMessage, ConstraintViolationType type)
            {
                string exceptionMessage = 
                    string.Format("Invariant '{0}' failed.", condition);

                if (!String.IsNullOrEmpty(additionalMessage))
                {
                    exceptionMessage += " " + additionalMessage;
                }

                // Optionally, the 'type' parameter can be used, but never 
                // throw an exception when the value of 'type' is unknown
                // or unvalid.
                throw new InvalidOperationException(exceptionMessage);
            }
        }
    }
}
VB.NET example
' TODO

The new 'Invariant' entry point method can be used as follows:

C# example
void Method()
{
    // Although IsGreaterThan is defined in the library,
    // you can use it with your new Invariant method.
    Invariants.Invariant(this.Count, "Count")
        .IsGreaterThan(0);
}
VB.NET example
' TODO

Last edited Aug 27, 2009 at 4:07 PM by dot_NET_Junkie, version 2

Comments

No comments yet.