如何转换Expr&lt;'a - &gt; “B个表达式<func <'a,obj =“”>&gt; </func <'a,>

时间:2012-05-18 05:32:48

标签: linq f#

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

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

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

mc.Arguments.[0] :?> LambdaExpression

...其中mcMethodCallExpression。例外是:

  

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

不,MethodCallExpressionN末尾的额外“N”不是拼写错误。有没有人有建议?感谢。

更新

这是一个完整的复制品。事实证明,此代码在<@ 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

1 个答案:

答案 0 :(得分:4)

您是否可以发布更完整的示例并包含您尝试转换的F#表达式?

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

  • 我创建了新的F#3.0项目,并从2.0版本的F#PowerPack中复制了Linq.fsLinq.fsi。 (或者在F#3.0的某个地方有ToLinqExpression方法的3.0版本吗?)

  • 我使用了来自Daniel's earlier answer的代码,并按如下方式调用了函数:

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

    这没有抛出任何异常,它打印x => (x + 1),这对我来说是正确的。

编辑:要回答更新的问题 - 您引用的两个代码示例(我的和Daniel的)假设引用的主体是显式构造的函数,因此它们只能用于特定结构的引用:<@ 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#表达式树进行后处理并删除这些构造)。