For a recent LINQ-based project I needed to merge branches of multiple expression trees into one.  Normally this doesn’t cause any issues, but I wanted to dynamically merge multiple ‘Where’ predicates together with arbitrary logical operators (hope that’s the right term; it sounds about right).  I found a great base class called ExpressionVisitor on Matt Warren’s blog.  Always keen to reduce the amount of typing (and thinking) I need to do, I couldn’t resist making a couple of helpers.  The first allows you to use syntax like this (don’t worry, it gets better…):

    MethodCallExpression call = ...;

    call = ExpressionVisitor<ParameterExpression>.Visit(
        call,
        p => p.Type == typeof( T ) ? elementParam : p ) as MethodCallExpression;

Basically it lets you specify a delegate to visit (and change) a single expression type in the tree.  Here’s the code (you’ll also need Matt’s ExpressionVisitor source):

    public class ExpressionVisitor<T> : ExpressionVisitor where T : Expression
    {
        Func<T, Expression> visitor;

        public ExpressionVisitor( Func<T, Expression> visitor )
        {
            this.visitor = visitor;
        }

        public static Expression Visit(
            Expression exp,
            Func<T, Expression> visitor )
        {
            return new ExpressionVisitor<T>( visitor ).Visit( exp );
        }

        public static Expression<TDelegate> Visit<TDelegate>(
            Expression<TDelegate> exp,
            Func<T, Expression> visitor )
        {
            return (Expression<TDelegate>)new ExpressionVisitor<T>( visitor ).Visit( exp );
        }

        protected override Expression Visit( Expression exp )
        {
            if ( exp is T && visitor != null ) exp = visitor( (T)exp );

            return base.Visit( exp );
        }
    }

As a refinement I added a couple of extension methods for more compact syntax.  For non-generic types you can do this:

    MethodCallExpression call = ...;

    call = call.Visit<ParameterExpression, MethodCallExpression>(
        p => p.Type == typeof( T ) ? elementParam : p );

For generic types you can do this:

    Expression<Func<Profile, bool>> call = ...;
call = call.Visit<ParameterExpression, Func<Profile, bool>>( p => p.Type == typeof( T ) ? elementParam : p );

(However I’d rather the Func<Profile, bool> wasn’t needed in the call to Visit<>() here…  I’d hoped it could infer the TDelegate type and let me just specify the ‘T’ type, but it seems it can’t do both together smile_sad.  Maybe I’m missing something obvious, or perhaps it’s a beta 2 bug?  Feedback welcome!).

Here’s the code for the extension methods:

    public static class ExpressionExtensions
    {
        public static Expression Visit<T>(
            this Expression exp,
            Func<T, Expression> visitor ) where T : Expression
        {
            return ExpressionVisitor<T>.Visit( exp, visitor );
        }

        public static TExp Visit<T, TExp>(
            this TExp exp,
            Func<T, Expression> visitor )
            where T : Expression
            where TExp : Expression
        {
            return (TExp)ExpressionVisitor<T>.Visit( exp, visitor );
        }

        public static Expression<TDelegate> Visit<T, TDelegate>(
            this Expression<TDelegate> exp,
            Func<T, Expression> visitor ) where T : Expression
        {
            return ExpressionVisitor<T>.Visit<TDelegate>( exp, visitor );
        }
    }

I’m sure there’s scope for other enhancements too… smile_regular

About these ads