我正在使用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
...其中mc
是MethodCallExpression
。例外是:
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
答案 0 :(得分:4)
您是否可以发布更完整的示例并包含您尝试转换的F#表达式?
我尝试使用最小样本测试.NET 4.5上的行为,它对我有用。这是我做的:
我创建了新的F#3.0项目,并从2.0版本的F#PowerPack中复制了Linq.fs
和Linq.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#表达式树进行后处理并删除这些构造)。