如何转换Expr< a-> 'b>表达式< Func< a,obj>> [英] How to Convert Expr<'a -> 'b> to Expression<Func<'a, obj>>
问题描述
我正在将F#3.0与.NET 4.5 beta一起使用,并且试图将类型为Expr<'a -> 'b>
的F#报价转换为LINQ Expression<Func<'a, 'b>>
.
I'm using F# 3.0 with .NET 4.5 beta, and I'm trying to convert an F# quotation of type Expr<'a -> 'b>
to a LINQ Expression<Func<'a, 'b>>
.
我发现了几个可以解决此问题的问题,但是这些技术似乎不再起作用,大概是由于F#3.0或.NET 4.5的变化.
I've found several questions that have solutions to this problem, but those techniques don't seem to work any longer, presumably due to changes in either F# 3.0 or .NET 4.5.
- Converting F# Quotations into LINQ Expressions
Expression<Func<T, bool>>
from a F# func
在两种情况下,当我从任一问题的解决方案中运行代码时,以下操作都会引发异常:
In both cases, when I run the code from the solutions of either question, the following action throws an exception:
mc.Arguments.[0] :?> LambdaExpression
...其中mc
是MethodCallExpression
.例外是:
...where mc
is a MethodCallExpression
. The exception is:
System.InvalidCastException:无法将类型为"System.Linq.Expressions.MethodCallExpressionN"的对象转换为类型为"System.Linq.Expressions.LambdaExpression".
System.InvalidCastException: Unable to cast object of type 'System.Linq.Expressions.MethodCallExpressionN' to type 'System.Linq.Expressions.LambdaExpression'.
否,MethodCallExpressionN
末尾的额外"N"不是错字.有人有建议吗?谢谢.
No, the extra "N" at the end of MethodCallExpressionN
is not a typo. Does anyone have a suggestion? Thanks.
更新
这是完整的复制品.事实证明,此代码在类似<@ fun x -> x + 1 @>
的表达式上可以正常工作.我的问题是,就我而言,我需要将Expr<'a -> 'b>
转换为Expr<'a -> obj>
,这样就不必用box
乱放所有的lambda表达式.我是通过将原始表达式拼接到这个表达式中来做到的:<@ %exp >> box @>
.这样会产生具有正确类型的对象,但是转换为Expression<Func<'a, obj>>
的代码不再起作用.
Here's a complete reproduction. It turns out this code works fine on an expression like <@ fun x -> x + 1 @>
. My problem is that in my case I need to convert an Expr<'a -> 'b>
into Expr<'a -> obj>
so that I don't have to litter all my lambda expressions with box
. I did so by splicing the original expression into this one: <@ %exp >> box @>
. This produces an object with the correct type, but the code to convert to Expression<Func<'a, obj>>
no longer works.
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
推荐答案
是否可以发布更完整的示例,并同时包含您要转换的F#表达式?
Can you post a more complete sample and also include the F# expression that you're trying to convert?
我尝试使用最少的示例测试.NET 4.5上的行为,它对我有用.这是我所做的:
I tried to test the behaviour on .NET 4.5 using a minimal sample and it worked for me. Here is what I did:
-
我创建了一个新的F#3.0项目,并从F#PowerPack的2.0版本复制了
Linq.fs
和Linq.fsi
. (或者在F#3.0中某个地方是否提供了ToLinqExpression
方法的3.0版本?)
I created new F# 3.0 project and copied
Linq.fs
andLinq.fsi
from the 2.0 version of F# PowerPack. (Or is there a 3.0 version of theToLinqExpression
method available somewhere in F# 3.0?)
我使用了 Daniel的早期答案中的代码,并调用了该函数如下:
I used the code from Daniel's earlier answer and called the function as follows:
let r = toLinq <@ fun x -> x + 1 @>
printfn "%A" r
这没有引发任何异常,它打印了x => (x + 1)
,对我来说看起来是正确的.
This did not throw any exception and it printed x => (x + 1)
, which looks correct to me.
要回答更新的问题-您引用的两个代码示例(mine和Daniel的示例)都假定引号的主体是显式构造的函数,因此它们仅适用于特定结构的引号:<@ fun x -> ... @>
.
To answer the updated question - both of the code samples that you referred to (mine and Daniel's) assume that the body of the quotation is an explicitly constructed function, so they only work on quotations of a specific structure: <@ fun x -> ... @>
.
您可以通过在显式构造的函数中使用拼接来解决此问题.以下对我有用:
You can fix the problem by using splicing in an explicitly constructed function. The following works for me:
let exp = <@ fun x -> x + 1 @>
let r = toLinq <@ fun a -> box ((%exp) a) @>
printfn "%A" r
这包含F#函数的应用程序,因此生成的Expression
包含对ToFSharpFunc
的调用(它将委托转换为F#函数),然后对其进行调用.如果您希望标准.NET工具可以理解Expression
,这可能是个问题(在这种情况下,您必须对C#表达式树进行后处理并删除这些构造).
This contains application of an F# function, so the generated Expression
contains a call to ToFSharpFunc
(which converts a delegate to an F# function) and then invocation of this. This may be an issue if you want Expression
that standard .NET tools can understand (in which case, you'd have to post-process the C# expression tree and remove these constructs).
这篇关于如何转换Expr< a-> 'b>表达式< Func< a,obj>>的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!