AFAIK The BCL has very limited support for working with Expressions. I'm afraid that you're going to have to rewrite the expression yourself to change the method parameter type.
It's not hard, but not easy either. Basically, you will clone every node of the Expression
(it's a tree) but set the root node's data type to your Func<TImplementation, bool>
.
I would look for a different design that accomplishes the same goal but doesn't have this casting requirement - plowing through Expressions is not fun.
Update I've implemented a function that does what you want. I call it CastParam
:
public static Expression<Func<TOut, bool>> CastParam<TIn, TOut>(this Expression<Func<TIn, bool>> inExpr) {
if (inExpr.NodeType == ExpressionType.Lambda &&
inExpr.Parameters.Count > 0) {
var inP = inExpr.Parameters[0];
var outP = Expression.Parameter(typeof(TOut), inP.Name);
var outBody = inExpr.Body.ConvertAll(
expr => (expr is ParameterExpression) ? outP : expr);
return Expression.Lambda<Func<TOut,bool>>(
outBody,
new ParameterExpression[] { outP });
}
else {
throw new NotSupportedException();
}
}
All it does is rewrite the expression substituting the old ParamaterType with the new type. Here is my little test:
class TInterface { public int IntVal; }
class TImplementation : TInterface { public int ImplVal; }
void Run ()
{
Expression<Func<TInterface, bool>> intExpr = (i => i.IntVal == 42);
Expression<Func<TImplementation, bool>> implExpr = intExpr.CastParam<TInterface, TImplementation> ();
Console.WriteLine ("{0} --> {1}", intExpr, implExpr);
var c = implExpr.Compile ();
Console.WriteLine (c.Invoke (new TImplementation { IntVal = 41, ImplVal = 42 }));
Console.WriteLine (c.Invoke (new TImplementation { IntVal = 42, ImplVal = 41 }));
}
As expected, it prints:
False
True
The code relies on an Expression
rewriter that I wrote (rewrites expression trees from the bottom up):
public static Expression Rewrite(this Expression exp, Func<Expression, Expression> c) {
Expression clone = null;
switch (exp.NodeType) {
case ExpressionType.Equal: {
var x = exp as BinaryExpression;
clone = Expression.Equal(Rewrite(x.Left,c), Rewrite(x.Right,c), x.IsLiftedToNull, x.Method);
} break;
case ExpressionType.MemberAccess: {
var x = exp as MemberExpression;
clone = Expression.MakeMemberAccess(Rewrite(x.Expression,c), x.Member);
} break;
case ExpressionType.Constant: {
var x = exp as ConstantExpression;
clone = Expression.Constant(x.Value);
} break;
case ExpressionType.Parameter: {
var x = exp as ParameterExpression;
clone = Expression.Parameter(x.Type, x.Name);
} break;
default:
throw new NotImplementedException(exp.NodeType.ToString());
}
return c(clone);
}
The rewriter is obviously incomplete and you'll need to finish it off.