2010-06-30 27 views
5

Tôi đang cố gắng khớp mẫu với một vài loại mà tôi quan tâm để tạo SQL. Lý tưởng nhất là tôi muốn làm điều này:Có cách nào trong F # để loại-kiểm tra đối với một loại chung chung mà không chỉ định loại cá thể không?

let rec getSafeValue record (prop: PropertyInfo) = 
    match prop.GetValue(record, null) with 
    | :? string as str -> "'" + str + "'" 
    | :? Option<_> as opt -> 
     match opt with 
     | Some v -> getSafeValue v prop 
     | None -> "null" 
    | _ as v -> v.ToString() 

Vấn đề là ở đây, loại tham số để Option<_> được hạn chế để phù hợp với các record, mà kết thúc lên được chỉ obj.

Tôi biết tôi có thể thực hiện một số kiểm tra dựa trên sự phản chiếu (kiểm tra xem đó là loại chung và đó là loại tùy chọn dựa trên tên), nhưng tôi muốn tránh điều đó nếu có khả thi.

Trả lời

9

Không, không có cách nào tốt để thực hiện việc này bằng cách sử dụng các cấu trúc dựng sẵn của F #. Tuy nhiên, bạn có thể xây dựng mô hình hoạt động tái sử dụng của riêng bạn cho các loại điều này:

open Microsoft.FSharp.Reflection 
open Microsoft.FSharp.Quotations 
open Microsoft.FSharp.Quotations.DerivedPatterns 
open Microsoft.FSharp.Quotations.Patterns 

let (|UC|_|) e o = 
    match e with 
    | Lambdas(_,NewUnionCase(uc,_)) | NewUnionCase(uc,[]) -> 
     if (box o = null) then 
     // Need special case logic in case null is a valid value (e.g. Option.None) 
     let attrs = uc.DeclaringType.GetCustomAttributes(typeof<CompilationRepresentationAttribute>, false) 
     if attrs.Length = 1 
      && (attrs.[0] :?> CompilationRepresentationAttribute).Flags &&& CompilationRepresentationFlags.UseNullAsTrueValue <> enum 0 
      && uc.GetFields().Length = 0 
     then Some [] 
     else None 
     else 
     let t = o.GetType() 
     if FSharpType.IsUnion t then 
      let uc2, fields = FSharpValue.GetUnionFields(o,t) 
      let getGenType (t:System.Type) = if t.IsGenericType then t.GetGenericTypeDefinition() else t 
      if uc2.Tag = uc.Tag && getGenType (uc2.DeclaringType) = getGenType (uc.DeclaringType) then 
      Some(fields |> List.ofArray) 
      else None 
     else None 
    | _ -> failwith "The UC pattern can only be used against simple union cases" 

Bây giờ chức năng của bạn có thể trông như thế này:

let rec getSafeValue (item:obj) = 
    match item with 
    | :? string as str -> "'" + str + "'" 
    | UC <@ Some @> [v] -> getSafeValue v 
    | UC <@ None @> [] -> "null" 
    | _ as v -> v.ToString() 
+0

đó là một cách gọn gàng để làm điều đó. tôi đã làm một cái gì đó tương tự. – kolosy

0

Điều này không thể hoạt động trong F # mà không hiệp phương sai. Giả sử bạn hài lòng với v thuộc loại obj, bạn muốn có thể xử lý Option<anything> như thể nó là Option<obj>. Nếu không hiệp phương sai, Option<anything>Option<obj> là các loại độc lập.

+0

Chỉ cần được rõ ràng, NET (và do đó F #) không thực sự có hiệp phương sai. –

+0

@GaneshSittampalam - .NET (tính đến 4.0) * có * có hiệp phương sai: http://msdn.microsoft.com/en-us/library/dd799517.aspx. Tôi nghĩ rằng lý do nó không được thực hiện trong F # 2.0 là vì F # đã chủ yếu nhắm mục tiêu .NET 2.0 tại thời điểm phát hành. –

0

Khi tôi đặt mã của bạn trong F # Interactive, có vẻ như làm cho 'ghi' một tham số chung. Có lẽ nó hoạt động khác nhau trong trình biên dịch bình thường. Dù sao, nó có thể chọn lên rằng obj loại do đối số đầu tiên của GetValue là loại obj.

Tôi rất tiếc, tôi không thể kiểm tra điều này ngay bây giờ, nhưng hãy thực hiện cú đánh này. Hàm box sử dụng thông số chung để có thể thực hiện thủ thuật.

let rec getSafeValue record (prop: PropertyInfo) = 
    match prop.GetValue(box record, null) with 
    | :? string as str -> "'" + str + "'" 
    | :? Option<_> as opt -> 
     match opt with 
     | Some v -> getSafeValue v prop 
     | None -> "null" 
    | _ as v -> v.ToString() 
+0

cùng một thỏa thuận - nó hạn chế cả bản ghi và chọn để nhập 'a – kolosy

0

Một số v -> getSafeValue v prop sẽ chỉ hoạt động nếu v cùng loại với bản ghi. (hoặc bắt nguồn từ loại đó) nếu không dòng đầu tiên sẽ thất bại. bạn không thể nói prop.GetValue (record, null) trừ khi thuộc tính được trỏ tới bởi prop có ý nghĩa (hay còn gọi là một phần của kiểu) trong ngữ cảnh của đối số đầu tiên.

Nếu đó là cùng loại bạn có thể làm:

let rec getSafeValue (record:'a) (prop: PropertyInfo) = 
    match prop.GetValue(box record, null) with 
    | :? string as str -> "'" + str + "'" 
    | :? Option<'a> as opt -> 
     match opt with 
     | Some v -> getSafeValue v prop 
     | None -> "null" 
    | _ as v -> v.ToString() 

nhưng nếu loại v có nguồn gốc từ 'là nó sẽ phù hợp với trường hợp cuối cùng để cho ở trên để làm việc họ sẽ cần phải được chính xác cùng loại

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