重写表达式以替换List.Contains为自定义方法
本文关键字:Contains 自定义方法 List 替换 表达式 重写 | 更新日期: 2023-09-27 18:23:44
为了拓宽我的技能范围,我正在学习如何重写表达式。
目标:给定一个表达式,我想用对自己的静态方法InList
的调用来替换List.Contains()
的实例。例如,以下两个表达式应该是等价的:
Expression<Func<Foo,bool>> expr1 = myRewriter.Rewrite(foo => fooList.Contains(foo));
Expression<Func<Foo,bool>> expr2 = foo => InList(foo, fooList);
我的尝试:我了解到,使用自定义ExpressionVisitor是在现有表达式的基础上创建新表达式的最佳方式。然而,我一直无法构建一个真正调用我的方法的新MethodCallExpression
。以下是我尝试过的:
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;
}
}
然而,当我通过new InListRewriter<Foo>().Rewrite(foo => fooList.Contains(foo))
调用时,我在Expression.Call
:期间得到一个InvalidOperationException
类型"MyNamespace.InListRewriter `1[MyNamespace.Foo]"上没有与提供的参数兼容的方法"InList"。
我甚至试着用一个非常通用的签名制作一个新的InList:
public static bool InList(params object[] things) {...}
但仍然收到了相同的错误。我做错了什么?我想做的事情有可能吗?
您的代码有一个大问题:它传递的参数不正确,特别是第一个参数。
您需要使用实际的参数node.Arguments[0]
,而不是Expression.Parameter(target.ParameterType, target.Name)
。
此外,我建议您使用不同的Expression.Call
过载:使用MethodInfo
的过载。
最后的代码看起来是这样的:
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.Arguments[0]; // The thing being searched for
var newMethod = GetType().GetMethod("InList",
BindingFlags.Static | BindingFlags.Public);
// Create new expression
var newExpression = Expression.Call(newMethod, target, sourceList);
return newExpression;
}