2012-05-18 24 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的代码中,现在它可以工作。谢谢! –

相关问题