6

Expr<'a -> 'b>.NET 4.5ベータ版でF#3.0を使用しており、タイプのF#引用符をLINQに変換しようとしていExpression<Func<'a, 'b>>ます。

この問題の解決策があるいくつかの質問を見つけましたが、おそらくF#3.0または.NET 4.5のいずれかが変更されたため、これらの手法は機能しなくなったようです。

どちらの場合も、どちらかの質問の解決策からコードを実行すると、次のアクションで例外がスローされます。

mc.Arguments.[0] :?> LambdaExpression

...どこにmcありますかMethodCallExpression。例外は次のとおりです。

System.InvalidCastException:タイプ'System.Linq.Expressions.MethodCallExpressionN'のオブジェクトをタイプ'System.Linq.Expressions.LambdaExpression'にキャストできません。

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

アップデート

これが完全な複製です。このコードは、のような式で正常に機能することがわかりました<@ 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
4

1 に答える 1

5

より完全なサンプルを投稿し、変換しようとしている F# 式も含めることはできますか?

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

  • 新しい F# 3.0 プロジェクトを作成し、F# PowerPack の 2.0 バージョンからコピーLinq.fsしました。(または、F# 3.0 のどこかで使用できるメソッドLinq.fsiの 3.0 バージョンはありますか?)ToLinqExpression

  • ダニエルの以前の回答のコードを使用し、次のように関数を呼び出しました。

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

    これは例外をスローせずx => (x + 1)、私には正しいように見える が出力されました。

編集:更新された質問に答えるために-参照したコードサンプル(私のものとダニエルのもの)は両方とも、引用の本文が明示的に構築された関数であると想定しているため、特定の構造の引用でのみ機能します: <@ fun x -> ... @>.

明示的に構築された関数でスプライシングを使用することにより、問題を解決できます。以下は私にとってはうまくいきます:

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

これには F# 関数の適用が含まれているため、生成さExpressionれた にはToFSharpFunc(デリゲートを F# 関数に変換する) への呼び出しと、これの呼び出しが含まれています。標準の .NET ツールが理解できるようにしたい場合、これは問題になる可能性がありますExpression(その場合、C# 式ツリーを後処理して、これらの構造を削除する必要があります)。

于 2012-05-18T09:11:15.390 に答える