2012-05-18 22 views
5

我正在使用F#3.0與.NET 4.5測試版,並且我試圖將Expr<'a -> 'b>類型的F#引用轉換爲LINQ Expression<Func<'a, 'b>>如何將EXPR <'a ->'b>轉換爲表達式<Func <'a, obj>>

我發現了幾個有解決此問題的問題,但這些技術似乎不再工作,可能是由於F#3.0或.NET 4.5中的更改。

在這兩種情況下,當我從任何一個問題的解決方案運行的代碼,下列行爲將引發異常:

mc.Arguments.[0] :?> LambdaExpression 

...其中mcMethodCallExpression。唯一的例外是:

System.InvalidCastException:無法轉換類型 'System.Linq.Expressions.MethodCallExpressionN' 的目的爲類型 'System.Linq.Expressions.LambdaExpression'。

不,在MethodCallExpressionN末尾多餘的「N」不是拼寫錯誤。有沒有人有建議?謝謝。

UPDATE

下面是一個完整再現。事實證明,這個代碼在<@ fun x -> x + 1 @>等表達式上工作正常。我的問題是,在我的情況下,我需要將Expr<'a -> 'b>轉換爲Expr<'a -> obj>,這樣我就不必將box中的所有lambda表達式都廢棄掉了。我通過將原始表達式拼接到這個表達式中來做到這一點:<@ %exp >> box @>。這會生成正確類型的對象,但要轉換爲Expression<Func<'a, obj>>的代碼不再有效。

module Expr = 
    open System 
    open System.Linq.Expressions 
    open Microsoft.FSharp.Quotations 
    open Microsoft.FSharp.Linq.QuotationEvaluation 

    let rec private translateExpr (linq:Expression) = 
     match linq with 
     | :? MethodCallExpression as mc -> 
      let le = mc.Arguments.[0] :?> LambdaExpression 
      let args, body = translateExpr le.Body 
      le.Parameters.[0] :: args, body 
     | _ -> [], linq 

    let ToFuncExpression (expr:Expr<'a -> 'b>) = 
     let args, body = expr.ToLinqExpression() |> translateExpr 
     Expression.Lambda<Func<'a, 'b>>(body, Array.ofList args) 

let exp = <@ fun x -> x + 1 @> 

let r = Expr.ToFuncExpression <@ %exp >> box @> 
printfn "%A" r 
+0

也許你會因爲使用無點式而受到懲罰。如果使用'<@ fun x ->%exp x |> box @>'來代替,會發生什麼?當你使用無點式樣時,你轉換的表達式不是lambda表達式,它是一個應用程序。 – kvb

+0

@kvb - 這是一個很好的想法,但是當我使用該構造時,它強調'%exp'並告訴我「這個值不是函數並且不能被應用」並拒絕編譯。 –

+0

但是,'<@ fun x -> x |>%exp |> box @>'不會編譯。不幸的是,當我嘗試轉換它時,它會得到相同的錯誤。 –

回答

4

您可以發佈更完整的示例,還包括您嘗試轉換的F#表達式嗎?

我試圖用最小的樣本測試.NET 4.5上的行爲,它對我很有幫助。下面是我做的:

  • 我創造了新的F#3.0項目,從2.0版本F#PowerPack中的複製Linq.fsLinq.fsi。 (或者是有一個3.0版本的ToLinqExpression方法的地方在F#3.0?)

  • 我使用Daniel's earlier answer代碼和調用的函數如下:

    let r = toLinq <@ fun x -> x + 1 @> 
    printfn "%A" r 
    

    這並沒有拋出任何異常,它打印x => (x + 1),這對我來說看起來是正確的。

編輯:要回答這個更新的問題 - 無論你提到(我和丹尼爾的)的代碼示例假設報價的身體是一個明確的構造函數,所以他們只能在報價工作具體結構如下:<@ fun x -> ... @>

您可以通過在明確構造的函數中使用拼接來解決問題。對我來說,以下工作:

let exp = <@ fun x -> x + 1 @> 
let r = toLinq <@ fun a -> box ((%exp) a) @> 
printfn "%A" r 

這包含F#功能的應用,所以產生Expression包含ToFSharpFunc通話(其中委託轉換爲F#功能),然後調用此。如果你想要標準的.NET工具可以理解的Expression(這種情況下,你必須後處理C#表達式樹並移除這些結構),這可能是一個問題。

+0

我已經更新了我的問題。事實證明,它可能不需要F#3或.NET 4.5,而是我使用了引用拼接的事實。是的,我像你一樣將powerpack Linq文件複製到我的項目中。 –

+0

@JoelMueller謝謝 - 是的,問題是你不會傳遞包含顯式lambda的引用。看到我修改後的答案。 –

+0

'translateExpr'函數仍然在更新的答案中拋出一個(不同的)錯誤,所以我將它切換到Daniel的代碼中,現在它可以工作。謝謝! –

相關問題