This is the last part of an update about
Mono.Linq.Expressions, a tiny helper library to complement the System.Linq.Expressions namespace for .net 4 and Mono.
And it's not really about Mono.Linq.Expressions itself. If you're interested about it though, you can read the part about
fluently creating expression trees, and the other one about
combining lambda expressions together.
### Proper generic constraints
Have a look at the signature of the factory method Expression.Lambda
:
public static Expression<TDelegate> Lambda<TDelegate> (
Expression body,
params ParameterExpression [] parameters) {}
And at the signature of Expression<T>:
public sealed class Expression<T> : LambdaExpression {}
Given the post title, and the <h3> a few lines up there, it's obvious where I'm going: those generic parameters have no constraint. A C# compiler will happily compile:
class Potato {}
Expression<Potato> e = Expression.Lambda<Potato> (
Expression.Constant (new Potato ()));
Of course this won't get you far, but it sure is a heart breaker that because of a little language oddity, you delay to until runtime the problem resolution: this code will throw, Potato sure isn't a Delegate type, and Expression.Lambda, the only way to create an Expression<T> is making sure of that.
So much for type safety, generics!
Another famous issue is the following. Again, for the compiler, it's perfectly legit to write:
int i;
if (!Enum.TryParse ("42", out i))
Console.WriteLine ("This is so unfair!");
And why is that? Because of the signature of Enum.TryParse:
public static bool TryParse<TEnum> (string value, out TEnum result) where TEnum : struct {}
To sum the issue up, C# doesn't allow generic constraints on delegates and enums. And why am I rumbling about this? Because Mono.Linq.Expressions has this method:
public static Expression<T> Combine<T> (
this Expression<T> self,
Func<Expression, Expression> combinator)
And if Expression<T> is not constrained on Delegate, then I sure as hell won't let this stand in my own public API! Which is why the actual signature of the Combine method is:
public static Expression<T> Combine<[DelegateConstraint] T> (
this Expression<T> self,
Func<Expression, Expression> combinator) where T : class
And all of this was just a perfect excuse to write a little Mono.Cecil based utility tool, appropriately named patch-constraints, that is used as a post-compile step to fixup the Delegate and Enum constraints of any generic parameter decorated respectively with a DelegateConstraintAttribute or EnumConstraintAttribute, turning them into actual constraints to delegates and enums, which, funnily enough, the C# compiler is more than happy to honor.
Obviously I could have used the most famous Jon Skeet's unconstrained-melody tool, but 2002 called and it wants its ildasm based, IL text parser back.