Jason Richmeier
Jason Richmeier

Reputation: 1645

Convert Lambda Expression Parameter Type

If I have the following classes...

public class Source
{
  public string Name
  {
    get;

    set;
  }
}

public class Destination
{
  public string Name
  {
    get;

    set;
  }
}

...and I have the following code to create an expression....

Expression<Func<Source, bool>> expression = e => e.Name == "Test;

...what I end up with is a lambda expression with a single parameter (e) of type Source. What I am trying to do is convert this expression to have a parameter of type Destination.

I have tried something like...

Expression<Func<Destination, bool>> expression2 = Expression.Lambda<Func<Destination, bool>>(expression.Body, Expression.Parameter(typeof(Destination), "e")));

...but this does not seem to work.

What is the best way to make this conversion. It seems like, from what I recall working with, the inner parts of the expression tree (in this case, the member expression) are bound to a particular type. Would I need to parse through the entire expression tree in order to make the conversion? Is there something simple I am missing?

Upvotes: 2

Views: 1558

Answers (2)

Ivan Stoev
Ivan Stoev

Reputation: 205539

Is there something simple I am missing?

If fact handling all the possible cases is not simple. A simplified function that supports only property/field accessors of the predicate parameter could be like this

public static class ExpressionUtils
{
    public static Expression<Func<TTarget, bool>> Convert<TSource, TTarget>(Expression<Func<TSource, bool>> source)
    {
        var parameter = Expression.Parameter(typeof(TTarget), source.Parameters[0].Name);
        var body = new ParameterConverter { source = source.Parameters[0], target = parameter }.Visit(source.Body);
        return Expression.Lambda<Func<TTarget, bool>>(body, parameter);
    }

    class ParameterConverter : ExpressionVisitor
    {
        public ParameterExpression source;
        public ParameterExpression target;
        protected override Expression VisitParameter(ParameterExpression node)
        {
            return node == source ? target : base.VisitParameter(node);
        }
        protected override Expression VisitMember(MemberExpression node)
        {
            return node.Expression == source ? Expression.PropertyOrField(target, node.Member.Name) : base.VisitMember(node);
        }
    }
}

And the usage with your sample would be:

Expression<Func<Source, bool>> source = e => e.Name == "Test;
var target = ExpressionUtils.Convert<Source, Destination>(source);

Upvotes: 1

CodeNotFound
CodeNotFound

Reputation: 23190

I don't know exactly what you are trying to do. I just understand that you are trying to make your lambda expression to be compatible with Source and Destination without creating two lambda expressions for each one.

Based on my understand.

First create an Interface like that :

public interface IName
{
    string Name { get; set; }
}

Then make Source and Destination to implement the new interface IName as follow:

public class Source : IName
{
    public string Name { get; set; }
}

public class Destination : IName
{
    public string Name { get; set; }
}

Then your lambda expression will simply look like this:

Expression<Func<IName, bool>> expression = e => e.Name == "Test";

this lambda expression is compatible with all class that impelment IName interface.

Upvotes: 1

Related Questions