2010-01-26 38 views
6

Trong đoạn sau, ý định của tôi là chuyển đổi System.Object (có thể là một FSharpList) thành một danh sách của bất kỳ kiểu generic nào mà nó đang nắm giữ.Làm thế nào để đưa một đối tượng vào danh sách loại chung trong F #

match o with 
    | :? list<_>    -> addChildList(o :?> list<_>) 
    | _      -> addChild(o) 

Đáng tiếc là chỉ list<obj> là bao giờ xuất hiện như một danh sách. Tôi muốn list<Foo> cũng được kết hợp dưới dạng danh sách.

Đối với một số ngữ cảnh, tôi đang cố gắng duyệt qua một cấu trúc đối tượng bằng cách phản ánh để xây dựng TreeView của lớp và con của nó. Hãy xem xét các lớp sau:

type Entity = { 
    Transform : Matrix 
    Components : obj list 
    Children : Entity list 
} 

Tôi muốn xây dựng một cây hiển thị cho tôi tất cả các lớp được chứa trong thực thể. Thông qua phản ánh, tôi có thể có được tất cả các thuộc tính của đối tượng và giá trị của chúng (Giá trị là quan trọng, vì tôi muốn hiển thị các phần tử khác nhau trong danh sách có thuộc tính Tên của phần tử nếu có):

 let o = propertyInfo.GetValue(obj, null) 

Giá trị này có thể là danh sách loại nào đó, nhưng giá trị trả lại chỉ là System.Object Tôi gặp sự cố khi cố chuyển đổi đối tượng này thành danh sách. Tôi buộc phải làm như sau:

 match o with 
     | :? list<obj>    -> addChildList(o :?> list<obj>) 
     | :? list<Entity>   -> addChildList(o :?> list<Entity>) 
     | _       -> addChild(o) 

Ở đây tôi phải chỉ định chính xác loại mà tôi đang cố chuyển đổi.
Tôi thực sự muốn viết những dòng này:

 match o with 
     | :? list<_>    -> addChildList(o :?> list<_>) 
     | _      -> addChild(o) 

Đáng tiếc là điều này chỉ phù hợp với từng trên list<obj>

+2

Bạn có thực sự cần danh sách đã nhập không? Dường như với tôi kết hợp 'IEnumerable' sẽ đủ. –

Trả lời

1

Nó chỉ ra rằng một trong hai list<'a> hoặc array<'a> có thể được xuất hiện như seq<obj>

match o with 
    | :? seq<obj> -> addChildCollection(o :?> seq<obj>) 
    | _   -> addChild(o) 

tôi không thực sự quan tâm rằng nó là một danh sách. Miễn là tôi có thể lặp lại nó.

+1

Điều đó phải hoạt động trên .NET 4.0, nhưng sẽ không hoạt động trên các phiên bản trước đó như 'seq <'a>' không được đánh dấu là biến thể. Ngoài ra, hãy nhớ rằng điều này sẽ chỉ hoạt động trên danh sách hoặc mảng chứa các loại tham chiếu (ví dụ:một 'danh sách ' có thể được coi là 'seq ', nhưng không thể liệt kê 'danh sách '. – kvb

+2

Ngoài ra, tôi nghĩ rằng nó sẽ là một chút sạch hơn để làm các mô hình phù hợp như '| :? seq là s -> addChildCollection (s) ', do đó bạn không có downcast rõ ràng. – kvb

+0

Bí quyết nhỏ nhặt này đã cứu được thịt xông khói của tôi khi tôi cần điều trị Danh sách <'a> và một số đồng nhất IE2 có thể đếm được <'a>. Cảm ơn! –

5

Thật không may, không có cách nào dễ dàng để làm những gì bạn muốn. Loại kiểm tra chỉ có thể được sử dụng với các loại cụ thể và thậm chí nếu kiểm tra loại được chuyển, toán tử chuyển đổi :?> cũng chỉ hoạt động để diễn đạt các loại cụ thể để phía bên tay phải của đối sánh của bạn sẽ không làm những gì bạn muốn. Bạn có thể phần nào làm việc xung quanh vấn đề này bằng một mô hình hoạt động:

open Microsoft.FSharp.Quotations 
open Microsoft.FSharp.Quotations.Patterns 

let (|GenericType|_|) = 
    (* methodinfo for typedefof<_> *) 
    let tdo = 
    let (Call(None,t,[])) = <@ typedefof<_> @> 
    t.GetGenericMethodDefinition() 
    (* match type t against generic def g *) 
    let rec tymatch t (g:Type) = 
    if t = typeof<obj> then None 
    elif g.IsInterface then 
     let ints = if t.IsInterface then [|t|] else t.GetInterfaces() 
     ints |> Seq.tryPick (fun t -> if (t.GetGenericTypeDefinition() = g) then Some(t.GetGenericArguments()) else None) 
    elif t.IsGenericType && t.GetGenericTypeDefinition() = g then 
     Some(t.GetGenericArguments()) 
    else 
     tymatch (t.BaseType) g 
    fun (e:Expr<Type>) (t:Type) -> 
    match e with 
    | Call(None,mi,[]) -> 
     if (mi.GetGenericMethodDefinition() = tdo) then 
      let [|ty|] = mi.GetGenericArguments() 
      if ty.IsGenericType then 
      let tydef = ty.GetGenericTypeDefinition() 
      tymatch t tydef 
      else None 
     else 
      None 
    | _ -> None 

mô hình hoạt động này có thể được sử dụng như sau:

match o.GetType() with 
| GenericType <@ typedefof<list<_>> @> [|t|] -> addChildListUntyped(t,o) 
| _           -> addChild(o) 

nơi bạn đã tạo ra một biến thể của addChildList mà phải mất một loại t và một đối tượng o (với loại thời gian chạy list<t>) thay vì sử dụng danh sách chung.

Đây là một chút lộn xộn, nhưng tôi không thể nghĩ ra giải pháp sạch hơn.

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