如何转换Expr< a-> 'b>表达式< Func< a,obj>> [英] How to Convert Expr<'a -> 'b> to Expression<Func<'a, obj>>

查看:86
本文介绍了如何转换Expr< a-> 'b>表达式< 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

...其中mcMethodCallExpression.例外是:

...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.fsLinq.fsi. (或者在F#3.0中某个地方是否提供了ToLinqExpression方法的3.0版本?)

  • I created new F# 3.0 project and copied Linq.fs and Linq.fsi from the 2.0 version of F# PowerPack. (Or is there a 3.0 version of the ToLinqExpression 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&lt; a-&gt; 'b&gt;表达式&lt; Func&lt; a,obj&gt;&gt;的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆