How to create an empty list of a certain type of runtime

Given a .Net type, say typeof<string> , at runtime, how do I create the equivalent of string list = [] ?

My motivation is that when using FSharpValue.MakeRecord to create a record based on the parsed values, the values ​​should be passed as obj[] . I finished the arguments with box and it worked, except for the lists. The problem I am facing is that an empty list of untyped lists cannot be put in a box and then unpacked. The specific error returned:

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

An empty typed list can be put in a box and unpacked, so I tried to find a way to make a list for the runtime type, for example. a Type returned by typeof <> but with no luck.

 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)} 
+6
source share
5 answers

Using reflection, you can get the List module and call the generic empty method:

 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, [||]) 

use the following:

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

If you call this quite often, consider caching (reduces the execution time by ~ 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, [||]) 
+4
source

@CaringDev using .NET reflection is fine, but you can also use the F # reflection module to instantiate union cases:

 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, [||]) 
+3
source

Let me add another alternative answer - although both existing methods work, they rely on an understanding of how F # presents lists. In the first case, you need to know that the Empty method exists, and in the second case, you need to know that there is an allied case with the name Empty .

Usually I prefer to do this by defining a helper type and using reflection by my custom type:

 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) 

This gives you a fairly simple function that can cache MethodInfo (you can even use Expression to pre-compile and cache calls) and do not rely on smart tricks.

+3
source

I was late for the party, but I tried to do the same. I found a way to do this using pattern matching:

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

This will give you an empty list of any type, if you can create a value of this type to start it.

+1
source

How to use Seq.cast to draw an empty general list?

 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
source

Source: https://habr.com/ru/post/1013367/


All Articles