2016-12-20 11 views
6

नेट टाइप को देखते हुए, typeof<string> कहें, रनटाइम पर कैसे string list = [] के बराबर बनाया गया है?किसी विशिष्ट रनटाइम प्रकार की खाली सूची कैसे बनाएं

मेरी प्रेरणा यह है कि FSharpValue.MakeRecord का उपयोग करते हुए मानों के आधार पर रिकॉर्ड बनाने के लिए मानों को पार्स किए गए मानों को obj[] के रूप में पारित करने की आवश्यकता है। मैं box का उपयोग करके तर्कों कास्टिंग कर रहा हूं और उसने सूचियों को छोड़कर काम किया है। मेरे सामने आने वाली समस्या यह है कि एक खाली अनियमित सूची सूची को बॉक्स नहीं किया जा सकता है और फिर अनबॉक्स किया जा सकता है। विशिष्ट लौटे त्रुटि है:

System.InvalidCastException: Unable to cast object of type 
'Microsoft.FSharp.Collections.FSharpList`1[System.Object]' 
to type 
'Microsoft.FSharp.Collections.FSharpList`1[System.String]'. 

एक खाली टाइप सूची बॉक्सिंग और अनबॉक्स्ड जा सकती है, तो मैं एक क्रम प्रकार, उदाहरण के लिए एक सूची कास्ट करने के लिए एक रास्ता खोजने के लिए कोशिश की है < टाइप करके एक प्रकार लौटाया गया है लेकिन बिना किसी किस्मत के।

type Test = {Names : string list} 
// fails 
let genericList = [] 
{Names = unbox (box genericList)} 

//works 
let typedList : string list = [] 
{Names = unbox (box typedList)} 

//works 
let genericNonEmptyList = ["Bill"] 
{Names = unbox (box genericNonEmptyList)} 
+5

यह प्रतिबिंब का उपयोग करके किया जा सकता है। हालांकि आम तौर पर यह संकेत मिलता है कि आपको –

+0

डिज़ाइन पर पुनर्विचार करना चाहिए, Seq.cast क्या आप चाहते हैं? क्षमा करें अगर मैं ओपी को गलत समझता हूं: 'जेनेरिकलिस्ट |> Seq.cast '। – s952163

+0

जॉन, कोई संकेत यह प्रतिबिंब के साथ कैसे करें?मैंने "अहम" को रास्ता तलाशने के बारे में काफी कुछ बताया है, लेकिन जहां तक ​​मैं देख सकता हूं कि ऐसा करने का कोई स्पष्ट तरीका नहीं है। – jbeeko

उत्तर

0

कैसे Seq.cast का उपयोग कर खाली सामान्य सूची कास्ट करने के लिए के बारे में?

type Test = {Names : string list} 
let genericList = [] 
let test = {Names = unbox (box genericList) |> Seq.cast<string> |> Seq.toList} 
test.Names //val it : string list = [] 
+0

वास्तव में '[] |> Seq.cast |> Seq.toList' काम करता है लेकिन केवल तभी संकलित समय प्रकार 'स्ट्रिंग' पता है। मेरे मामले में मैं रनटाइम पर एक सूची बना रहा हूं जिसे रनटाइम 'टाइप' में पारित किया गया है। इस मामले में मैं उपर्युक्त नहीं कर सकता क्योंकि टाइप से प्राप्त एक रनटाइम प्रकार का उपयोग नहीं किया जा सकता है। – jbeeko

+0

मैं देखता हूं। मुझे डर था कि यह एक मुद्दा होगा। :-( – s952163

3

प्रतिबिंब का उपयोग करके आप List मॉड्यूल हो और सामान्य empty विधि कॉल कर सकते हैं: इस प्रकार

open System 
open System.Reflection 

let emptyList (t:Type) = 
    Assembly.GetAssembly(typeof<_ list>) 
     .GetType("Microsoft.FSharp.Collections.ListModule") 
     .GetMethod("Empty", BindingFlags.Static ||| BindingFlags.Public) 
     .MakeGenericMethod(t) 
     .Invoke(null, [||]) 

उपयोग:

let itemType = typeof<string> 
let emptyStringList = emptyList(itemType) :?> string list 

आपको लगता है कि कॉल कर रहे हैं अक्सर, कैशिंग पर विचार (~ 1/3 द्वारा निष्पादन समय को कम करता है):

let emptyList = 
    let empty = 
     Assembly.GetAssembly(typeof<_ list>) 
      .GetType("Microsoft.FSharp.Collections.ListModule") 
      .GetMethod("Empty", BindingFlags.Static ||| BindingFlags.Public) 
    fun (t:Type) -> empty.MakeGenericMethod(t).Invoke(null, [||]) 
3

@CaringDev के जवाब का उपयोग कर नेट प्रतिबिंब ठीक है, लेकिन आप भी एफ # विशिष्ट प्रतिबिंब मॉड्यूल का उपयोग संघ मामलों के उदाहरण बनाने के लिए कर सकते हैं:

let empty ty = 
    let uc = 
     Reflection.FSharpType.GetUnionCases(typedefof<_ list>.MakeGenericType [|ty|]) 
     |> Seq.filter (fun uc -> uc.Name = "Empty") 
     |> Seq.exactlyOne 
    Reflection.FSharpValue.MakeUnion(uc, [||]) 
+0

मुझे यह पसंद है कि यह संक्षिप्त कैसे है और इस तथ्य का उपयोग करता है कि सूचियां यूनियन प्रकार हैं। 'प्रतिबिंब.एफएसएचआरपी टाइप। गेटयूनीओन कैसेसिंग (टाइपपीफॉफ <_ list>' @ caringdev द्वारा सुझाव के समान प्रदर्शन प्रदर्शन? – jbeeko

+1

नहीं, जेनेरिक के विपरीत 'MethodInfo', जेनेरिक 'UnionCaseInfo' जैसी कोई चीज़ नहीं है, इसलिए कोई समान दृष्टिकोण नहीं है। गैर-सामान्य मामलों में, आप शायद' FSharpValue.PreComputeUnionConstructor' के परिणाम को 'FSharpValue' के बजाय कैश करना और उपयोग करना चाहते हैं। MakeUnion' – kvb

3

मुझे एक और विकल्प जवाब जोड़ते हैं - हालांकि दोनों मौजूदा विधियों का काम करते हैं, वे समझते हैं कि एफ # सूचियों का प्रतिनिधित्व कैसे करता है। पहले मामले में, आपको यह जानने की जरूरत है कि Empty विधि है और दूसरे मामले में, आपको यह जानने की जरूरत है कि Empty नामक यूनियन केस है।

मैं आम तौर पर एक सहायक प्रकार को परिभाषित करने और अपने कस्टम प्रकार से अधिक प्रतिबिंब का उपयोग करके ऐसा करना पसंद करते हैं:

type ListHelper = 
    static member Empty<'T>() : list<'T> = [] 

let makeEmpty = 
    let empty = typeof<ListHelper>.GetMethod("Empty") 
    let emptyArr : obj[] = [| |] 
    fun ty -> empty.MakeGenericMethod([| ty |]).Invoke(null, emptyArr) 

यह आपको काफी सरल समारोह देता है कि कैश कर सकते MethodInfo (आप भी Expression इस्तेमाल कर सकते हैं पूर्व के लिए संकलन और नकल कैश) और चालाक चाल पर भरोसा नहीं करता है।

+1

यह एक अच्छा विकल्प है, लेकिन रनटाइम पर यूनियन केस का नाम निर्धारित करना मुश्किल नहीं है: 'नाम = चलो (कोटेशन। पैटर्न। न्यूयूनीयनकेस (uc, _)) = <@ [] @> uc.Name' में। – kvb

+0

दुर्भाग्यवश @kvb और @CaringDev के समाधान की तरह यह काफी सही काम नहीं कर रहा है। उदाहरण के लिए 'बनाने के प्रकार ' चलाना एक हस्ताक्षर वापस देता है 'वैल यह: obj = [] 'लेकिन क्या आवश्यक है' वैल यह है: स्ट्रिंग सूची = [] ' – jbeeko

+1

@jbeeko आप परिणाम डालने के लिए':?> स्ट्रिंग सूची 'जोड़ सकते हैं - लेकिन यदि आप हैं प्रतिबिंब का उपयोग करके, मुझे लगता है कि आप तत्वों के प्रकार को स्थिर रूप से नहीं जानते हैं (आपके पास केवल रनटाइम 'System.Type' है)। यदि आप स्थिर रूप से प्रकार जानते थे, तो आपको प्रतिबिंब की आवश्यकता नहीं होगी। –

1

मैं पार्टी के लिए देर से हूं, लेकिन मैं वही काम करने की कोशिश कर रहा था।

let emptyListFromExample e = 
    match [e] with 
    | [] -> [] 
    | x::xs -> xs 

यह आपको किसी भी प्रकार की एक खाली सूची आप इसे बंद शुरू करने के लिए उस प्रकार के एक मूल्य के निर्माण कर सकते हैं प्रदान की दे देंगे: मैं एक तरह से पैटर्न मिलान के साथ ऐसा कर पाया।

संबंधित मुद्दे