2012-05-18 46 views
5

Tôi đang sử dụng F # 3.0 với .NET 4.5 beta và tôi đang cố chuyển đổi báo giá F # loại Expr<'a -> 'b> thành LINQ Expression<Func<'a, 'b>>.Cách chuyển đổi Expr <'a -> 'b> thành Biểu thức <Func <'a, obj>>

Tôi đã tìm thấy một số câu hỏi có giải pháp cho vấn đề này, nhưng những kỹ thuật đó dường như không hoạt động nữa, có lẽ do thay đổi trong F # 3.0 hoặc .NET 4.5.

Trong cả hai trường hợp, khi tôi chạy mã từ các giải pháp của một trong hai câu hỏi, các hành động sau ném một ngoại lệ:

mc.Arguments.[0] :?> LambdaExpression 

... nơi mcMethodCallExpression. Ngoại lệ là:

System.InvalidCastException: Không thể truyền đối tượng thuộc loại 'System.Linq.Expressions.MethodCallExpressionN' vào loại 'System.Linq.Expressions.LambdaExpression'.

Không, dấu "N" bổ sung ở cuối MethodCallExpressionN không phải là lỗi đánh máy. Còn ai có đề nghị nào không? Cảm ơn.

CẬP NHẬT

Đây là một bản tái tạo hoàn chỉnh. Nó chỉ ra mã này hoạt động tốt trên một biểu thức như <@ fun x -> x + 1 @>. Vấn đề của tôi là trong trường hợp của tôi, tôi cần phải chuyển đổi một số Expr<'a -> 'b> thành Expr<'a -> obj> để tôi không phải vứt bỏ tất cả các biểu thức lambda của tôi với box. Tôi đã làm như vậy bằng cách ghép các biểu thức ban đầu vào một trong những: <@ %exp >> box @>. Điều này tạo ra một đối tượng với đúng loại, nhưng mã để chuyển đổi thành Expression<Func<'a, obj>> không còn hoạt động nữa.

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

Có thể bạn đang bị phạt vì sử dụng phong cách không có điểm. Điều gì sẽ xảy ra nếu bạn sử dụng hộp "<@ fun x ->% exp x |> @>' thay thế? Khi bạn sử dụng kiểu không có điểm, biểu thức bạn đang chuyển đổi không phải là lambda, đó là một ứng dụng. – kvb

+0

@kvb - Đó là một suy nghĩ tốt, nhưng khi tôi sử dụng cấu trúc đó, nó gạch chân '% exp' và nói với tôi" Giá trị này không phải là một hàm và không thể được áp dụng "và từ chối biên dịch. –

+0

Tuy nhiên, '<@ fun x -> x |>% exp |> box @> 'hiện biên dịch. Thật không may, nó nhận được lỗi tương tự khi tôi cố gắng chuyển đổi nó. –

Trả lời

4

Bạn có thể đăng mẫu hoàn chỉnh hơn và cũng bao gồm biểu thức F # mà bạn đang cố chuyển đổi không?

Tôi đã thử kiểm tra hành vi trên .NET 4.5 bằng cách sử dụng mẫu tối thiểu và nó đã hoạt động đối với tôi. Dưới đây là những gì tôi đã làm:

  • Tôi tạo ra F # 3.0 dự án mới và sao chép Linq.fsLinq.fsi từ phiên bản 2.0 của F # PowerPack. (Hoặc là có một phiên bản 3.0 của ToLinqExpression phương pháp có sẵn ở đâu đó trong F # 3.0?)

  • tôi đã sử dụng mã từ Daniel's earlier answer và gọi hàm này như sau:

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

    này không ném bất kỳ ngoại lệ và nó in x => (x + 1), có vẻ đúng với tôi.

EDIT: Để trả lời câu hỏi được cập nhật - cả hai mẫu mã mà bạn gọi (mỏ và Daniel) cho rằng cơ thể của báo giá là một chức năng được xây dựng một cách rõ ràng, vì vậy họ chỉ làm việc trên trích dẫn của một cấu trúc cụ thể: <@ fun x -> ... @>.

Bạn có thể khắc phục vấn đề bằng cách sử dụng nối trong một hàm được xây dựng rõ ràng. Các công trình sau đây cho tôi:

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

này chứa ứng dụng của một chiếc F # chức năng, do đó tạo ra Expression chứa gọi ToFSharpFunc (mà chuyển đổi một đại biểu cho một chiếc F # chức năng) và sau đó gọi về điều này. Đây có thể là vấn đề nếu bạn muốn Expression các công cụ .NET chuẩn có thể hiểu được (trong trường hợp này, bạn phải xử lý cây biểu thức C# và loại bỏ các cấu trúc này).

+0

Tôi đã cập nhật câu hỏi của mình. Nó chỉ ra nó có thể không phải làm với F # 3 hoặc .NET 4.5, mà là thực tế là tôi đã sử dụng nối ngoặc kép. Và có, tôi đã sao chép các tệp LINQ powerpack vào dự án của tôi giống như bạn đã làm. –

+0

@JoelMueller Cảm ơn - vâng, vấn đề là bạn không vượt qua nó báo giá có chứa lambda rõ ràng. Xem câu trả lời đã sửa đổi của tôi. –

+0

Chức năng 'translateExpr' vẫn đang ném một lỗi (khác) với câu trả lời cập nhật của bạn, vì vậy tôi đã chuyển nó sang mã của Daniel, và bây giờ nó hoạt động. Cảm ơn! –

Các vấn đề liên quan