如何将Expr <'a - >'b>转换为Expression <Func <'a,obj >>

Joe*_*ler 5 linq f#

我正在使用F#3.0和.NET 4.5 beta,我正在尝试将F#类型的引用转换Expr<'a -> 'b>为LINQ Expression<Func<'a, 'b>>.

我发现了几个可以解决这个问题的问题,但这些技术似乎不再起作用,可能是由于F#3.0或.NET 4.5的变化.

在这两种情况下,当我从任一问题的解决方案运行代码时,以下操作会引发异常:

mc.Arguments.[0] :?> LambdaExpression
Run Code Online (Sandbox Code Playgroud)

...其中mc一个MethodCallExpression.例外是:

System.InvalidCastException:无法将类型为"System.Linq.Expressions.MethodCallExpressionN"的对象强制转换为"System.Linq.Expressions.LambdaExpression".

不,最后的额外"N" MethodCallExpressionN不是拼写错误.有没有人有建议?谢谢.

UPDATE

这是一个完整的复制品.事实证明这个代码在类似的表达式上运行良好<@ fun x -> x + 1 @>.我的问题是,在我的情况,我需要一个转换Expr<'a -> 'b>Expr<'a -> obj>这样我就不必垃圾与我所有的lambda表达式box.我通过将原始表达式拼接到这个表达式中来实现:<@ %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
Run Code Online (Sandbox Code Playgroud)

Tom*_*cek 5

您能否发布更完整的示例并包含您要转换的 F# 表达式?

我尝试使用最小样本测试 .NET 4.5 上的行为,它对我有用。这是我所做的:

  • 我创建了新的 F# 3.0 项目并从 F# PowerPack 2.0 版本Linq.fs复制。(或者F# 3.0 中Linq.fsi是否有该方法的 3.0 版本?)ToLinqExpression

  • 我使用了Daniel 之前的答案中的代码,并按如下方式调用了该函数:

    let r = toLinq <@ fun x -> x + 1 @>
    printfn "%A" r
    
    Run Code Online (Sandbox Code Playgroud)

    这没有引发任何异常,并且打印了x => (x + 1),这对我来说看起来是正确的。

编辑:为了回答更新的问题 - 您提到的两个代码示例(我的和丹尼尔的)都假设引用的主体是显式构造的函数,因此它们仅适用于特定结构的引用:<@ fun x -> ... @>

您可以通过在显式构造的函数中使用拼接来解决该问题。以下对我有用:

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

这包含 F# 函数的应用程序,因此生成的Expression包含对ToFSharpFunc(将委托转换为 F# 函数)的调用,然后调用此函数。如果您希望标准 .NET 工具能够理解,这可能是一个问题Expression(在这种情况下,您必须对 C# 表达式树进行后处理并删除这些构造)。