2016-12-20 30 views
6

.Net Türü verildiğinde, çalışma zamanında typeof<string> deyin, string list = [] eşdeğeri nasıl oluşturulur?Belirli bir çalışma zamanı türünün boş bir listesi nasıl oluşturulur

Benim motivasyonum, kullanılarak ayrıştırılan değerlere dayalı bir kayıt oluşturmak için değerlerin obj[] olarak iletilmesi gerektiğidir. Ben box kullanarak argümanlar döküm oldum ve listeleri ile dışında çalıştı. Karşılaştığım sorun, boş bir liste dışı listenin kutulu ve kutudan çıkarılmamasıdır. döndürülen hata:

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

Boş yazılan liste kutulu ve kutusuz edilebilir bir çalışma zamanı tipi, örn için bir liste yayınlamak için bir yol bulmaya çalıştık böylece <> 'dan türeyen bir tür, ancak şanssız.

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

Bu, yansıma kullanılarak yapılabilir. Bununla birlikte, genel olarak, yeniden düşünmeniz gerektiğini belirten –

+0

Seq.cast, ne istediğinizi yapmaz mı? Özür dilerim: OP: 'genericList |> Seq.cast '. – s952163

+0

John, bunun yansımasıyla nasıl yapılacağı hakkında ipuçları var mı?Ben "ahem" biraz yoldan bakmaya başladım ama görebildiğim kadarıyla bunu yapmanın açık bir yolu yok. – jbeeko

cevap

0

Boş genel listeyi dökmek için Seq.cast'u kullanmaya ne dersiniz? aşağıdaki gibi

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

kullanımını: Eğer oldukça sık aradığınız takdirde

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

, önbelleğe alma düşünün

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

Indeed [] |> Seq.cast |> Seq.toList işlevi, ancak "string" derleme zamanı türünü biliyorsanız çalışır. Benim durumumda, çalışma zamanında 'Tür' çalışma zamanında geçirilmiş bir liste yapıyorum. Bu durumda, yukarıdaki gibi yapamam çünkü bir çalışma zamanı türü, türünden ne tür bir dosya kullanamazsınız. – jbeeko

+0

Anladım. Korkarım ki bu bir sorun olacak. :-( – s952163

3

Eğer jenerik empty yöntemi List modülünü almak ve çağırabilir yansımasını kullanma (yürütme süresini ~ 1/3 oranında azaltır):

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 'ın cevabı kullanarak .NET yansıma iyidir, ama aynı zamanda sendika vakalarının örneklerini oluşturmak için F #' e özgü yansıma modülünü kullanabilirsiniz:

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

Bunun ne kadar özlü olduğunu ve Listelerin birleşim türleri olduğu gerçeğini kullanmayı severim. Reflection.FSharpType.GetUnionCases (typedefof <_ list> "önbelleği, @caringdev tarafından yapılan öneriye benzer şekilde yardımcı olur? – jbeeko

+1

Hayır, 'MethodInfo', jenerik bir 'UnionCaseInfo' diye bir şey yoktur, bu yüzden benzer bir yaklaşım yoktur.Amaç dışı durumlarda, 'FSharpValue.PreComputeUnionConstructor' sonucunu FSharpValue çağırmak yerine önbelleğe almak ve kullanmak isteyebilirsiniz. MakeUnion ' – kvb

3

beni bir daha alternatif cevabı ekleyelim - hem rağmen Mevcut yöntemlerin çalışması, F # listelerini nasıl temsil ettiğine güveniyorlar. İlk durumda, Empty yöntemini bilmeniz gerekir ve ikinci durumda, Empty adı verilen bir sendika vakası olduğunu bilmeniz gerekir.

Ben genellikle yardımcı türünü tanımlayan ve benim özel türü üzerinden yansıma kullanarak bunu yapmayı tercih

: Bu, oldukça basit işlevi verir

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) 

önbelleğe alabilir MethodInfo (hatta alanı ön Expression kullanabilirsiniz çağırır derler ve önbellekleri) ve akıllı hilelere dayanmaz.

+1

Bu iyi bir alternatif, ancak çalışma zamanında bir sendika vakasının adını belirlemek zor değil: 'isim ver = izin ver (Quotations.Patterns.NewUnionCase (uc, _)) = <@ [] @> uc.Name' içinde. – kvb

+0

Ne yazık ki, Kvb ve @ CaringDev'in çözümleri gibi, bu doğru olanı yapmak gibi görünmüyor. Örneğin 'makeEmpty typer ' imzayı döndürür 'val it: obj = [] 'ancak gerekli olan nedir? val it: dize listesi = []' – jbeeko

+1

@jbeeko Sonuç vermek için ':?> dize listesi' ekleyebilirsiniz. yansımayı kullanarak, o zaman elementlerin türlerini statik olarak bilmediğinizi varsaydım (sadece runtime 'System.Type' var). Eğer türünü statik olarak biliyor olsaydınız, yansımaya hiç gerek duymazdınız. –

1

Partiye geç kaldım ama aynı şeyi yapmaya çalışıyordum.

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

Bu size bunu başlatmak için bu tür bir değerini oluşturabilirsiniz sağlanan her türlü boş bir liste, verecektir: Ben desen eşleştirme ile bunu yapmanın bir yolunu buldu.

İlgili konular