簡體   English   中英

重寫表達式以使用自定義方法替換List.Contains

[英]Rewrite expression to replace List.Contains with custom method

為了擴大我的技能,我正在努力學習如何重寫表達式。

目標:給定一個表達式,我想通過調用我自己的靜態方法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)) InvalidOperationException時,我在Expression.Call期間得到一個InvalidOperationException

類型'MyNamespace.InListRewriter`1 [MyNamespace.Foo]'上的方法'InList'與提供的參數兼容。

我甚至嘗試使用非常通用的簽名創建一個新的InList:

public static bool InList(params object[] things) {...}

但仍然收到同樣的錯誤。 我究竟做錯了什么? 我甚至想做什么?

您的代碼有一個大問題:它傳遞的參數不正確,特別是第一個。

而不是Expression.Parameter(target.ParameterType, target.Name)您需要使用實際參數: node.Arguments[0]

此外,我建議你使用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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM