In an attempt to expand my skill set, I am trying to learn how to rewrite expressions.
Purpose: Given the expression, I want to replace the List.Contains() instances with a call to my own static InList method. For example, the following two expressions should be equivalent:
Expression<Func<Foo,bool>> expr1 = myRewriter.Rewrite(foo => fooList.Contains(foo)); Expression<Func<Foo,bool>> expr2 = foo => InList(foo, fooList);
My attempt: I found out that using custom ExpressionVisitor is the best way to create a new expression based on an existing expression. However, I was unable to create a new MethodCallExpression that actually calls my method. Here is what I tried:
public class InListRewriter<T> : ExpressionVisitor { public static bool InList(T target, List<T> source) { // this is my target method return true; } public Expression<Func<T, bool>> Rewrite(Expression<Func<T, bool>> expression) { return Visit(expression) as Expression<Func<T,bool>>; } protected override Expression VisitMethodCall(MethodCallExpression node) { // Only rewrite List.Contains() if (!node.Method.Name.Equals("Contains", StringComparison.InvariantCultureIgnoreCase)) return base.VisitMethodCall(node); // Extract parameters from original expression var sourceList = node.Object; // The list being searched var target = node.Method.GetParameters()[0]; // The thing being searched for // Create new expression var type = typeof (InListRewriter<T>); var methodName = "InList"; var typeArguments = new Type[] { }; var arguments = new[] { Expression.Parameter(target.ParameterType, target.Name), sourceList }; var newExpression = Expression.Call(type, methodName, typeArguments, arguments); return newExpression; } }
However, when I call this through new InListRewriter<Foo>().Rewrite(foo => fooList.Contains(foo)) , I get an InvalidOperationException during Expression.Call :
There is no 'InList' method in type 'MyNamespace.InListRewriter`1 [MyNamespace.Foo]' is compatible with the arguments provided.
I even tried creating a new InList with an extremely common signature:
public static bool InList(params object[] things) {...}
But still the same error was received. What am I doing wrong? Is that what I want to do even possible?