2012-05-18 18 views
5

Sto usando F # 3.0 con .NET 4.5 beta, e sto cercando di convertire un F # citazione di tipo Expr<'a -> 'b> ad un LINQ Expression<Func<'a, 'b>>.Come convertire Espr <'a -> 'b> per Expression <Func <'a, obj>>

Ho trovato diverse domande che hanno soluzioni a questo problema, ma quelle tecniche non sembrano funzionare più, presumibilmente a causa di modifiche in F # 3.0 o .NET 4.5.

In entrambi i casi, quando si esegue il codice dalle soluzioni di due domande, l'azione seguente genera un'eccezione:

mc.Arguments.[0] :?> LambdaExpression 

... dove mc è un MethodCallExpression. L'eccezione è:

System.InvalidCastException: Impossibile eseguire il cast oggetto di tipo 'System.Linq.Expressions.MethodCallExpressionN' digitare 'System.Linq.Expressions.LambdaExpression'.

No, la "N" in più alla fine di MethodCallExpressionN non è un errore di battitura. Qualcuno ha un suggerimento? Grazie.

UPDATE

Ecco una riproduzione completa. Risulta che questo codice funziona bene su un'espressione come <@ fun x -> x + 1 @>. Il mio problema è che nel mio caso ho bisogno di convertire un Expr<'a -> 'b> in Expr<'a -> obj> in modo che non debba sporcare tutte le mie espressioni lambda con box. L'ho fatto collegando l'espressione originale a questo: <@ %exp >> box @>. Questo produce un oggetto con il tipo corretto, ma il codice da convertire in Expression<Func<'a, obj>> non funziona più.

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

Forse sei stato punito per il tuo uso di stile point-free. Cosa succede se usi '<@ fun x ->% exp x |> box @>'? Quando usi lo stile point-free, l'espressione che stai convertendo non è una lambda, è un'applicazione. – kvb

+0

@kvb - Questa è una buona idea, ma quando uso quel costrutto, sottolinea '% exp' e mi dice" Questo valore non è una funzione e non può essere applicato "e si rifiuta di compilare. –

+0

Tuttavia, '<@ fun x -> x |>% exp |> box @>' compila. Sfortunatamente ottiene lo stesso errore quando provo a convertirlo. –

risposta

4

Puoi pubblicare un esempio più completo e includere anche l'espressione F # che stai cercando di convertire?

Ho provato a testare il comportamento su .NET 4.5 utilizzando un campione minimo e ha funzionato per me. Ecco quello che ho fatto:

  • ho creato nuove # 3.0 progetto F e copiati Linq.fs e Linq.fsi dalla versione 2.0 di F # PowerPack. (O c'è una versione 3.0 del metodo ToLinqExpression disponibile da qualche parte in F # 3.0?)

  • ho usato il codice da Daniel's earlier answer e chiamato la funzione come segue:

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

    Questo non gettare alcuna eccezione e ha stampato x => (x + 1), che mi sembra corretto.

EDIT: Per rispondere alla domanda aggiornato - entrambi gli esempi di codice che lei ha fatto riferimento a (mia e Daniel) supporre che il corpo della citazione è una funzione esplicitamente costruito, in modo che funzionano solo sulle quotazioni di una struttura specifica: <@ fun x -> ... @>.

È possibile risolvere il problema utilizzando lo splicing in una funzione costruita in modo esplicito. Le seguenti opere per me:

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

Questa contiene applicazione di una funzione F #, in modo che il generato Expression contiene una chiamata a ToFSharpFunc (che converte un delegato a una funzione F #) e poi l'invocazione di questo. Questo potrebbe essere un problema se si desidera che lo standard di strumenti .NET possa comprendere Expression (nel qual caso, si dovrà elaborare l'albero di espressioni C# e rimuovere questi costrutti).

+0

Ho aggiornato la mia domanda. Si scopre che potrebbe non avere a che fare con F # 3 o .NET 4.5, ma piuttosto il fatto che ho usato lo splicing delle quotazioni. E sì, ho copiato i file powerpack Linq nel mio progetto proprio come hai fatto tu. –

+0

@JoelMueller Grazie - sì, il problema è che non si passa la citazione contenente lambda esplicito. Vedi la mia risposta modificata. –

+0

La funzione 'translateExpr' stava ancora lanciando un (diverso) errore con la tua risposta aggiornata, quindi l'ho passato al codice di Daniel, e ora funziona. Grazie! –

Problemi correlati