2012-05-18 12 views
5

estoy usando C# .NET 3.0 con 4.5 beta, y estoy tratando de convertir un F # cotización del tipo Expr<'a -> 'b> a un LINQ Expression<Func<'a, 'b>>.Cómo convertir Expr <'a -> 'b> para la expresión <Func <'a, obj>>

He encontrado varias preguntas que tienen soluciones para este problema, pero esas técnicas ya no parecen funcionar, presumiblemente debido a cambios en F # 3.0 o .NET 4.5.

En ambos casos, cuando corro el código de las soluciones de cualquiera de estas preguntas, la siguiente acción se produce una excepción:

mc.Arguments.[0] :?> LambdaExpression 

... donde mc es un MethodCallExpression. La excepción es:

System.InvalidCastException: No se puede convertir objeto de tipo 'System.Linq.Expressions.MethodCallExpressionN' al tipo 'System.Linq.Expressions.LambdaExpression'.

No, la "N" adicional al final de MethodCallExpressionN no es un error tipográfico. alguien tiene una sugerencia? Gracias.

ACTUALIZACIÓN

Aquí es una reproducción completa. Resulta que este código funciona bien en una expresión como <@ fun x -> x + 1 @>. Mi problema es que en mi caso necesito convertir un Expr<'a -> 'b> en Expr<'a -> obj> para que no tenga que ensuciar todas mis expresiones lambda con box. Lo hice empalmando la expresión original en este: <@ %exp >> box @>. Esto produce un objeto con el tipo correcto, pero el código para convertir a Expression<Func<'a, obj>> ya no funciona.

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

Quizás esté siendo castigado por el uso de estilo sin puntos. ¿Qué sucede si utiliza '<@ fun x ->% exp x |> box @>' en su lugar? Cuando utilizas un estilo sin puntos, la expresión que estás convirtiendo no es una lambda, es una aplicación. – kvb

+0

@kvb - Es una buena idea, pero cuando uso esa construcción, subraya '% exp' y me dice" Este valor no es una función y no se puede aplicar "y se niega a compilar. –

+0

Sin embargo, '<@ fun x -> x |>% exp |> box @>' se compila. Lamentablemente, obtiene el mismo error cuando intento convertirlo. –

Respuesta

4

¿Se puede publicar una muestra más completa y también incluir la expresión F # que está tratando de convertir?

Traté de probar el comportamiento en .NET 4.5 usando una muestra mínima y funcionó para mí. Esto es lo que hice:

  • creé nuevo proyecto # F 3.0 y copiar Linq.fs y Linq.fsi de la versión 2.0 de C# PowerPack. (¿O hay una versión 3.0 del método ToLinqExpression disponible en algún lugar de F # 3.0?)

  • he utilizado el código de Daniel's earlier answer y llamó a la función de la siguiente manera:

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

    Esto no lanzó ninguna excepción y imprimió x => (x + 1), lo cual me parece correcto.

EDIT: Para responder a la pregunta actualizada - ambos de los ejemplos de código que lo refirió a (la mía y Daniel) asumen que el cuerpo de la cita es una función construida de forma explícita, por lo que sólo funcionan en las cotizaciones de una estructura específica: <@ fun x -> ... @>.

Puede solucionar el problema utilizando el empalme en una función explícitamente construida. Las siguientes obras para mí:

let exp = <@ fun x -> x + 1 @> 
let r = toLinq <@ fun a -> box ((%exp) a) @> 
printfn "%A" r 

Este contiene la aplicación de una función F #, por lo que el Expression generado contiene una llamada a ToFSharpFunc (que convierte un delegado a una función F #) y luego la invocación de este. Esto puede ser un problema si desea Expression que las herramientas .NET estándar puedan comprender (en cuyo caso, deberá postprocesar el árbol de expresiones C# y eliminar estas construcciones).

+0

He actualizado mi pregunta. Resulta que puede no tener que ver con F # 3 o .NET 4.5, sino con el hecho de que utilicé el splicing de comillas. Y sí, copié los archivos de Powerpack Linq en mi proyecto al igual que tú. –

+0

@JoelMueller Gracias - sí, el problema es que no lo pasa cita que contiene explícita lambda. Ver mi respuesta modificada. –

+0

La función 'translateExpr' seguía arrojando un error (diferente) con su respuesta actualizada, así que lo cambié al código de Daniel, y ahora funciona. ¡Gracias! –

Cuestiones relacionadas