2012-04-14 20 views
1

Ich möchte einen einfachen Datensatztyp basierend auf Feldern erstellen.Erstellen von Datensatztyp in F #

Das heißt:

let rectype = MakeRecordType(['fieldname1'; 'fieldname2']) 

direkt Anbieter geben gehen sieht aus wie schwer gunpower für eine so einfache Aufgabe.

Gibt es einen anderen Weg?

Update

Ich habe folgende Fragen gefunden, die sehr ähnlich aussehen Creating F# record through reflection

+2

Ich verstehe nicht, wie würden Sie einen Datensatztyp verwenden, die sonst dynamisch erstellt wurde? Im Gegensatz zu: 'type MyRecord = {field1: string, field2: string}' – yamen

+0

@yamen würde ich es genauso verwenden wie jeden anderen Typ. – nicolas

+0

Was ich meine ist - wenn Sie eine dynamische Liste von Eigenschaften bereitstellen, verwenden Sie sie dann dynamisch oder statisch? Wenn es statisch ist, warum sollte es nicht zuerst statisch definiert werden? – yamen

Antwort

5

beiseite Putting den Nutzen des Endergebnisses, ein Ausschnitt unten erreicht genau das, was Sie im Geiste der my other related answer gestellt:

#if INTERACTIVE 
#r @"C:\Program Files (x86)\Microsoft F#\v4.0\FSharp.Compiler.dll" 
#r @"C:\Program Files (x86)\FSharpPowerPack-1.9.9.9\bin\FSharp.Compiler.CodeDom.dll" 
#endif 

open System 
open System.CodeDom.Compiler 
open Microsoft.FSharp.Compiler.CodeDom 
open Microsoft.FSharp.Reflection 

type RecordTypeMaker (typeName: string, records: (string*string) []) = 
    let _typeDllName = "Synth"+typeName+".dll" 
    let _code = 
     let fsCode = new System.Text.StringBuilder() 
     fsCode.Append("module ").Append(typeName).Append(".Code\ntype ").Append(typeName).Append(" = {") |> ignore 
     for rec' in records do fsCode.Append(" ").Append(fst rec').Append(" : ").Append(snd rec').Append(";\n") |> ignore 
     fsCode.Append("}").ToString() 
    let _compiled = 
     use provider = new FSharpCodeProvider() 
     let options = CompilerParameters([||], _typeDllName) 
     let result = provider.CompileAssemblyFromSource(options, [|_code|]) 
     result.Errors.Count = 0 
    let mutable _type: Type = null 

    member __.RecordType 
     with get() = if _compiled && _type = null then 
         _type <- Reflection.Assembly.LoadFrom(_typeDllName).GetType(typeName+".Code+"+typeName) 
        _type 

Eine Skizze Umsetzung RecordTypeMaker nimmt eine willkürliche Record type Definition mit type name und Array von field names begleitet von field type names. Dann wird hinter dem Vorhang ein Stück F # -Code zusammengesetzt, das den angeforderten Datensatztyp definiert, kompiliert diesen Code über CodeDom provider, lädt Container-Assembly und bietet Zugriff auf diesen neu erstellten synthetischen Datensatztyp über Reflection. Ein Test-Schnipsel

let myType = RecordTypeMaker("Test", [|("Field1", "string"); ("Field2", "int")|]).RecordType 
printfn "IsRecordType=%b" (FSharpType.IsRecord(myType)) 
printfn "Record fields: %A" (FSharpType.GetRecordFields(myType)) 

demonstriert für einen rein synthetischen Typ myType der Proof of Concept:

IsRecordType=true 
Record fields: [|System.String Field1; Int32 Field2|] 
+0

ziemlich cool. Ich bin aber famazed, ich machte 2005 die gleichen ausgefallenen Hosentricks in csharp, um dynamische linq-Abfragen zu bekommen. 7 Jahre später sind wir fast an der gleichen Stelle bei der Erstellung eines zusammensetzbaren Typs ... – nicolas

+1

@nicolas: F # und C# sind _statisch typisierte Sprachen_. Die Erstellung von Runtime-Typen ist nicht einfach und nicht ohne Grund. – ildjarn

+0

@ildjam und Typ Anbieter gibt es auch aus einem Grund .. – nicolas

Verwandte Themen