
Controversial change in CuttingEdge.Conditions
In this post I’ll describe a controversial change I’m making in my open source project, CuttingEdge.Conditions.
For a long time I had my doubts about a particular part of the API of my CuttingEdge.Conditions library and these doubts were the main reason that kept me from releasing a stable version of CuttingEdge.Conditions. I decided to try to get in contact with users of my library to find out what they thought of this particular issue I was having. This lead to the following controversial change:
I’m removing the extension method behavior of the Requires() and Ensures() entry point methods.
This means that the following syntax is not supported anymore:
x.Requires().IsNotNull();
Instead the snippet above has to be written as follows:
Condition.Requires(x).IsNotNull();
This decision is controversial, because the whole concept of the library was based on this initial syntax and I know many developers like this fluent way of writing validations. There are however several issues with this syntax, so please bear with me, as I’ll try to explain why I decided to make this change.
The coming stable release of CuttingEdge.Conditions will contain this change.
Please note that this post also supplies some code that you can add to your project that allows you to revert to the old syntax.
Five reasons to change
There are mainly five reasons why I decided to drop the extension behavior on the Requires() and Ensures() entry point methods.
1. Not all .NET languages fully support the old syntax.
The Requires() and Ensures() methods extended System.Object and VB.NET can not handle extension methods on System.Object due to its late binding capabilities. Calling obj.Requires() would result in a runtime exception. For this reason the Conditions homepage on CodePlex already used the Condition.Requires syntax in the VB examples. Because of this, the Framework Design Guidelines warn against the use of extension methods on the type System.Object.
2. The extension methods clutter IntelliSense.
The Requires() and Ensures() method are shown in the IntelliSense drop down list for each and every object in the IDE; even when a developer isn't validating. This clutters the IntelliSense and this could confuse developers.
3. C# code snippets are less productive when generated with extension methods.
While the code snippets engine of the VB.NET IDE allows the insertion of namespaces, the C# IDE does not have this ability (and we shouldn't expect such a feature to be added soon). Normally, this isn't a big problem, because the C# IDE makes it easy to include a missing namespace, by pressing CTRL + DOT, ENTER on a type. When using extension methods however, the C# IDE is unable to determine the namespace of the extension method (because there is no type specified explicitly). This means that a developer must still add the CuttingEdge.Conditions namespace manually on the top of the file, after using one of the code snippets. However, when the code snippets generate 'Condition.Requires(x, "x")', a developer can move his cursor to the unknown Condition type and let the IDE insert the missing namespace very quickly.
4. Recognizing blocks of pre- and postconditions is easier with the new syntax.
This is actually a argument I heard from several developers, who use Conditions in their production code. Although x.Requires().IsNotNull() syntax is very readable as an individual line, most methods will have several lines of preconditions, and those developers noted that the Condition.Requires(x) syntax groups those statements very nicely together. I must say I agree with them.
5. Leaving extension methods out of the library allows developers to choose one over the other.
It's actually pretty easy (as I will show shortly) to add some code in your own project that defines Requires() and Ensures() extension methods. However, when Conditions defines those methods as extension methods, developers have no possibility to remove them (unless of course they create a private build of the library). An option would be to define the extension methods in another namespace within the Conditions assembly, as the Framework Design Guidelines advice, but I’m not fond of that idea in this situation. This means that the developers using extension methods, have to include two namespaces in each file and this is less intuitive.
I hope I've convinced you that this change is for the better, but if you, despite my arguments, still want the old syntax, please include the following code in your project. Most convenient is to put this class in the root namespace of your project.
using CuttingEdge.Conditions;
/// <summary>
/// This class defines extension methods for the
/// CuttingEdge.Conditions Requires and Ensures entry point
/// methods.
/// </summary>
internal static class ConditionExtensions
{
/// <summary>
/// Returns a new <see cref="ConditionValidator{T}" /> that allows
/// you to validate the preconditions of the given argument, given
/// it a default ArgumentName of 'value'.
/// </summary>
public static ConditionValidator<T> Requires<T>(this T value)
{
return Condition.Requires<T>(value);
}
/// <summary>
/// Returns a new <see cref="ConditionValidator{T}" /> that allows
/// you to validate the preconditions of the given argument.
/// </summary>
public static ConditionValidator<T> Requires<T>(this T value,
string argumentName)
{
return Condition.Requires<T>(value, argumentName);
}
/// <summary>
/// Returns a new <see cref="ConditionValidator{T}" /> that allows
/// you to validate the given argument, given it a default
/// ArgumentName of 'value'.
/// </summary>
public static ConditionValidator<T> Ensures<T>(this T value)
{
return Condition.Ensures<T>(value);
}
/// <summary>
/// Returns a new <see cref="Validator{T}">Validator</see> that
/// allows you to validate the postconditions of the given object.
/// </summary>
public static ConditionValidator<T> Ensures<T>(this T value,
string argumentName)
{
return Condition.Ensures<T>(value, argumentName);
}
}
As always: Happy validating ;-)
- .NET General, C#, CuttingEdge.Conditions, LINQ - No comments / No trackbacks - § ¶