4

Is there any simple way to convert

Expression<Func<TBase,bool>> 

to

Expression<Func<T,bool>>

where T is inherited from TBase?

Vladimir
  • 2,082
  • 1
  • 13
  • 27

3 Answers3

3

As long as T derives from TBase, you can directly create an expression of your desired type with the body and parameters of your original expression.

Expression<Func<object, bool>> x = o => o != null;
Expression<Func<string, bool>> y = Expression.Lambda<Func<string, bool>>(x.Body, x.Parameters);
2

You may have to convert manually. The reason for this is that you're effectively converting to a subset of what it could be. All T are TBase, but not all TBase are T.

The good news is that you can probably do it using Expression.Invoke, and apply the appropriate cast/conversion to TBase manually (of course catching any type safety issues).

Edit: I apologize for misunderstanding the direction you wanted to go in. I think simply converting the expression is still your best route either way. It gives you the ability to handle the conversion however you want. Marc Gravell's answer here is the most compact and clear way I've seen to do it.

Community
  • 1
  • 1
drharris
  • 11,194
  • 5
  • 43
  • 56
  • why manually? I convert E to E>, so in every place where I use TBase I can use T. – Vladimir May 30 '11 at 14:37
  • That's my bad. I misunderstood the direction you wanted to conversion to go. My original answer is updated with a link to a better answer than I would be able to come up with. – drharris May 30 '11 at 15:21
0

To make this I wrote ExpressionVisitor with overloading VisitLambda and VisitParameter

Here it is:

public class ConverterExpressionVisitor<TDest> : ExpressionVisitor
{
    protected override Expression VisitLambda<T>(Expression<T> node)
    {
        var readOnlyCollection = node.Parameters.Select(a => Expression.Parameter(typeof(TDest), a.Name));
        return Expression.Lambda(node.Body, node.Name, readOnlyCollection);
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        return Expression.Parameter(typeof(TDest), node.Name);
    }
}

public class A { public string S { get; set; } }
public class B : A { }

static void Main(string[] args)
{
    Expression<Func<A, bool>> ExpForA = a => a.S.StartsWith("Foo");
    Console.WriteLine(ExpForA); // a => a.S.StartsWith("Foo");

    var converter = new ConverterExpressionVisitor<B>();
    Expression<Func<B, bool>> ExpForB = (Expression<Func<B, bool>>)converter.Visit(ExpForA);
    Console.WriteLine(ExpForB); // a => a.S.StartsWith("Foo"); - same as for A but for B
    Console.ReadLine();
}
Vladimir
  • 2,082
  • 1
  • 13
  • 27