2010-01-26 17 views
6

में जेनेरिक प्रकार की सूची में ऑब्जेक्ट को कैसे डाला जाए, निम्न स्निपेट में मेरा इरादा एक सिस्टम को परिवर्तित करना है। ऑब्जेक्ट (जो एक FSharpList हो सकता है) जो भी सामान्य प्रकार की होल्डिंग की सूची में है।एफ #

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

दुर्भाग्य से केवल list<obj> कभी एक सूची के रूप में मिलान किया जाता है। मुझे list<Foo> भी एक सूची के रूप में मिलान किया जाना चाहिए।

कुछ संदर्भों के लिए, मैं कक्षा और उसके बच्चों के वृक्ष दृश्य बनाने के लिए प्रतिबिंब द्वारा किसी ऑब्जेक्ट संरचना को पार करने की कोशिश कर रहा हूं। निम्नलिखित वर्ग पर विचार करें:

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

मैं एक पेड़ बनाना चाहता हूं जो मुझे इकाई में निहित सभी वर्ग दिखाता है। प्रतिबिंब के माध्यम से, मैं किसी ऑब्जेक्ट के सभी गुणों और उनके मानों को भी प्राप्त कर सकता हूं (मान महत्वपूर्ण है, क्योंकि मैं तत्व की नाम संपत्ति के साथ सूची में विभिन्न तत्वों को प्रदर्शित करना चाहता हूं यदि यह एक है):

 let o = propertyInfo.GetValue(obj, null) 

यह मान किसी प्रकार की सूची हो सकता है, लेकिन मूल्य वापसी केवल एक सिस्टम है। ऑब्जेक्ट इस ऑब्जेक्ट को किसी सूची में कनवर्ट करने का प्रयास करते समय मैं समस्याओं में भाग लेता हूं। मैं बिल्कुल प्रकार है कि मैं करने के लिए कन्वर्ट करने के लिए कोशिश कर रहा हूँ में बताने की

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

यहाँ: मैं निम्न कार्य करने के लिए मजबूर कर रहा हूँ।
मैं वास्तव में इस लिखने के लिए करना चाहते हैं:

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

दुर्भाग्य से यह केवल कभी list<obj>

+2

क्या आपको वास्तव में टाइप की गई सूची की आवश्यकता है? ऐसा लगता है कि मुझे 'आईनेमेरेबल' से मेल खाना पर्याप्त होगा। –

उत्तर

1

ऐसा लगता है कि या तो list<'a> या array<'a> रूप seq<obj>

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

मिलान किया जा सकता मैं वास्तव में परवाह नहीं है कि यह एक सूची है। जब तक मैं इसे खत्म कर सकता हूं।

+1

यह .NET 4.0 पर काम करना चाहिए, लेकिन पिछले संस्करणों पर काम नहीं करेगा क्योंकि 'seq <'a>' को कॉन्वेंट के रूप में चिह्नित नहीं किया गया है। साथ ही, ध्यान रखें कि यह केवल सूचियों या सरणी पर संदर्भ प्रकारों (जैसे उदा।एक 'सूची ' को 'seq 'के रूप में माना जा सकता है, लेकिन' सूची 'नहीं हो सकता है)। – kvb

+2

इसके अलावा, मुझे लगता है कि यह पैटर्न मिलान करने के लिए थोड़ा साफ होगा जैसे '| : seq एस -> addChildCollection (ओं) के रूप में 'इसलिए आपके पास कोई स्पष्ट डाउनकास्ट नहीं है। – kvb

+0

इस छोटी सी चाल ने केवल मेरी बेकन को बचाया जब मुझे सूची <'a> और एक आईनेमेरेबल <'a> समरूप रूप से इलाज करने की आवश्यकता थी। धन्यवाद! –

5

पर से मेल खाता है दुर्भाग्य से, वहाँ कोई आसान तरीका है कि तुम क्या चाहते हो करने के लिए है। टाइप टेस्ट का उपयोग केवल विशिष्ट प्रकारों के साथ किया जा सकता है, और यदि टाइप टेस्ट पास हो गया है, तो रूपांतरण ऑपरेटर :?> भी विशिष्ट प्रकारों पर अभिव्यक्तियों को डालने के लिए काम करता है, इसलिए आपके मैच का दायां हाथ वही नहीं करेगा जो आप चाहते हैं।

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 

यह सक्रिय पद्धति का अनुसरण के रूप में इस्तेमाल किया जा सकता है: आप आंशिक रूप से एक सक्रिय पद्धति का उपयोग कर इस समस्या के समाधान कर सकते हैं

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

आप addChildList जो एक प्रकार लेता t और की भिन्नता बना लिया है जहां एक सामान्य सूची लेने के बजाय एक ऑब्जेक्ट o (रनटाइम प्रकार list<t> के साथ)।

यह थोड़ा सा गुंजाइश है, लेकिन मैं क्लीनर समाधान के बारे में नहीं सोच सकता।