2012-05-18 9 views
5

私は.NET 4.5ベータでF#3.0を使用しており、タイプExpr<'a -> 'b>のF#見積もりをLINQ Expression<Func<'a, 'b>>に変換しようとしています。Expr 'b>をExpression <Funcに変換する方法

この問題の解決策がいくつか見つかりましたが、F#3.0または.NET 4.5のいずれかの変更により、これらのテクニックはもう機能していないようです。どちらの場合も

私はどちらかの質問の解決策からのコードを実行すると、以下のアクションが例外をスロー:

mc.Arguments.[0] :?> LambdaExpression 

... mcMethodCallExpressionです。例外は次のとおりです。

System.InvalidCastExceptionの: 'System.Linq.Expressions.LambdaExpression' を入力するタイプ 'System.Linq.Expressions.MethodCallExpressionN' のオブジェクトをキャストすることができません。

いいえ、MethodCallExpressionNの末尾に余分な "N"はタイプミスではありません。誰にでも提案はありますか?ありがとう。

UPDATE

はここで完全再現です。このコードは、<@ fun x -> x + 1 @>のような式で正常に動作します。私の問題は、私の場合、Expr<'a -> 'b>Expr<'a -> obj>に変換する必要があるので、すべてのラムダ式をboxで捨てる必要はありません。私は元の表現をこれにスプライシングすることでそうしました:<@ %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

おそらく、あなたはポイントフリースタイルの使用のために罰せられているでしょう。代わりに '<@ fun x ->%exp x |> box @>'を使用するとどうなりますか?ポイントフリースタイルを使用する場合、変換する式はラムダではなく、アプリケーションです。 – kvb

+0

@kvb - これは良い考えですが、私がその構造体を使用すると、 '%exp'に下線が引かれ、"この値は関数ではなく、適用できません "と伝えられ、コンパイルが拒否されます。 –

+0

しかし、 '<@ fun x -> x |>%exp |> box @>'はコンパイルされます。残念ながら、変換しようとすると同じエラーが発生します。 –

答えて

4

さらに詳しいサンプルを投稿して、変換しようとしているF#式も含めることができますか?

最小限のサンプルを使用して.NET 4.5の動作をテストしようとしましたが、それは私のために働いていました。ここに私がやったことです:

  • 私は新しいF#3.0のプロジェクトを作成し、F#PowerPackにの2.0バージョンからLinq.fsLinq.fsiをコピーしました。 (または使用可能ToLinqExpression方法の3.0バージョンがどこかにF#3.0であるのか?)

  • 私はDaniel's earlier answerからコードを使用して、次のように関数を呼び出し:

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

    これは、任意の例外をスローしませんでしたし、私には正しいと思われるx => (x + 1)が印刷されました。

EDIT:更新疑問に答えるために - あなたは(私とダニエル)と呼ばれるコードサンプルの両方は、引用のボディを明示的に構築機能であると仮定ので、彼らは唯一の引用に取り組みます特定構造のもの:<@ fun x -> ... @>

明示的に構成された関数でスプライシングを使用することで問題を解決できます。私にとっては、次の作品:

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

これは、F#関数のアプリケーションが含まれているので、生成されたExpressionは、(F#関数にデリゲートを変換)ToFSharpFuncにコールしてから、このの呼び出しが含まれています。これは、標準の.NETツールが理解できるExpression(この場合、C#の式ツリーを後処理してこれらの構文を削除する必要がある)が必要な場合には問題になる可能性があります。

+0

私は自分の質問を更新しました。 F#3や.NET 4.5とは関係ないかもしれませんが、引用のスプライシングを使用したという事実が分かります。そして、はい、Powerpack Linqファイルを自分のプロジェクトにコピーしました。 –

+0

@JoelMuellerありがとうございます - はい、問題は明示的なラムダを含む引用符を渡さないことです。私の修正された答えを見てください。 –

+0

'translateExpr'関数は、あなたの更新された答えと(別の)エラーを投げていたので、ダニエルのコードに切り替えました。ありがとう! –

関連する問題