2013-07-18 27 views
4

更換List.Contains在試圖拓寬了我的技能,我想學習如何改寫表達式。重寫表達與自定義的方法

目標:給出一個表達式,我想用自己的靜態方法InList來調用List.Contains()的實例。例如,以下兩個表達式應該是等價的:

Expression<Func<Foo,bool>> expr1 = myRewriter.Rewrite(foo => fooList.Contains(foo)); 
Expression<Func<Foo,bool>> expr2 = foo => InList(foo, fooList); 

我嘗試:我已經學會了使用custom 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

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

我甚至嘗試做一個新的InList中有一個非常通用的簽名:

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

但還是得到了同樣的錯誤。我究竟做錯了什麼?我甚至想做甚麼?

+1

代碼看起來OK。查看您作爲參數傳入的表達式的類型。它們是否匹配(Foo,列表)? – usr

+0

@usr實施丹尼爾的回答,發現後,我有了第二個問題,事實證明,我的第二個問題是,我不小心被引用'foo.SomeParam',而不是'foo',因此類型不匹配而我得到的錯誤。所以你也是對的。 – ean5533

回答

4

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

相反的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; 
} 
+0

確實如此,但是'Expression.Call'調用仍然應該通過,因爲類型匹配。 – usr

+0

@usr:我不認爲OP得到的例外來自他展示的代碼。當我執行他的代碼,我得到了一個完全不同的一個,可更好地符合實際的問題,他的代碼:*出現InvalidOperationException:類型的變量「項目」「UserQuery +富」的範圍「」引用,但它沒有定義*。用方法調用表達式的第一個參數解決問題可以消除錯誤並且代碼正常工作。 –

+0

更正所有帳戶。我改變了我的訪問者以符合你的要求,並開始接收不同的錯誤。然而,將我的代碼放到一個更簡單的文件中允許它正確運行,所以在我的重寫器之外,我的代碼還有其他錯誤。我會最終弄明白。無論如何,你肯定回答了我的問題,謝謝。 – ean5533