2016-05-13 9 views
2

Lassen Sie uns sagen, dass wir die folgende Art haben, die nichts für den Augenblick tutF # generische Typ Instanciation von Objekt und geben Sie nicht

type Foo<'a,'b> = 
    new() = {} 

Wir wollen es instanziiert wie diese

type First = class end 

let first = new Foo<First,First>() 

Und was ich will gerade jetzt zu tun ist, das nächste Objekt instanziieren etwas wie

let second = new Foo<first.GetType(),First>() 

sein aber ich kann nicht Tun Sie first.GetType(), um es mit einem Typ zu versehen. Also dachte ich, auf einen Wert von Typ die Art der Bindung System.Type und verwenden Sie es wie folgt aus

let typing = first.GetType() 
let second = new Foo<typing,First>() 

Aber es sagt, dass der Typ typing nicht definiert ist, statt die Art der first nehmen. Wie kann ich etwas wie let second = new Foo<first.getType(),First>() machen?

Edit: Mehr Details hier über das, was ich versuche zu zielen: wir sagen, dass wir ein Protokoll der Kommunikation auf einem Kanal channel haben. Das Protokoll der Kommunikation wird unter Verwendung einer Karte definiert wie folgt aus:

[(currentState:Int , nextState:Int , type:string , label:string) -> realType:Type ] 

zum Beispiel

[(currentState:1 , nextState:2 , type:"send" , label:"hello()") -> realType:Hello ; 
    (currentState:2 , nextState:3 , type:"receive" , label:"bye()") -> realType:Bye] 

Was ich aus, dass wollen, ist folgende Funktionen

send(a:Hello) 
receive(a:Bye) 

aber so zu erzeugen, dass , wie Sie in der Karte sehen können, receive(a:Bye) kann nicht vor send(a:Hello) getan werden, oder es wird einen Fehler bei der Kompilierung geben. Es sollte der richtigen Reihenfolge folgen. Das ist der Grund, warum zu tun, dass ich eine Art von Typ receiveType als Rückgabewert der send(a:Hello) Funktion instanziieren will mich erlaubt, die receive(a:Bye) auf folgende Weise zu verwenden:

channel.send(Hello()).receive(Bye()) 

Die letzte Sache ist, dass die Karte kann haben mehr Staaten als diese 2, die Länge hängt von dem Protokoll ab, das ich verwende. Und die ganze Idee wird in einem Typ-Provider implementiert, so dass ich Typen und Methoden bereitstellen kann, die ich gerade mit intelliSense beschrieben habe.

Verwandte Frage: F# generating types in a type extension function within a type provider

+2

Sie können das nicht tun. Wie können Sie einen solchen Typ verwenden, ohne zu wissen, was die Typparameter statisch sind? – Lee

+0

Nun, ich dachte, dass ich das in einem Typ Provider verwenden würde. Und ich dachte auch, dass es vielleicht in einem Typ Provider verwendet wird und der Typ Provider kompiliert wird, wenn Sie es in einem Skript verwenden. Das würde bedeuten, dass der Typ bereits definiert ist. Aber jetzt, da ich darüber nachdenke, werde ich dem Typ Provider einen Parameter geben und dann vom Parameter diese Typen erstellen, so dass es nicht möglich ist. Aber dann kann diese Art von Code nur in einer dynamischen Sprache verwendet werden, ist das korrekt wie Python? Und was meinst du mit Typenparametern hier? – Leleutch

+1

Nach Typ-Parametern meine ich die Typen, die Sie für ''a' 'und' 'b' 'angeben, wenn Sie eine Instanz von' Foo 'definieren. Sie müssen für beide einen statischen Typ deklarieren, den Sie mit 'First' gemacht haben, d. H.' New Foo '. Aber 'first.GetType()' gibt ein Objekt zurück, das zur Laufzeit einen Typ darstellt. Python kann diese Beziehung nicht darstellen, da Python überhaupt keine Typen hat, aber es ist schwierig zu wissen, was Sie mit diesem Beispiel machen wollen. Versuchen Sie einen Typ Provider zu schreiben oder benötigen Sie nur einen generischen Datentyp? – Lee

Antwort

2

eine Instanz eines generischen Typs, deren Typ Argumente unterscheiden sich von denen eines bestehenden Objekts Instanziierungsanwendung kann zur Laufzeit mit den Methoden GetGenericTypeDefinition und MakeGenericType erfolgen.

So, da Ihr Beispiel:

type Foo<'a,'b> = 
    new() = {} 

type First = class end 

let first = new Foo<First,First>() 

Die Schritte sind:

  1. den generischen Typ ohne jegliche Argumente Get:

    let genericFooType = first.GetType().GetGenericTypeDefinition() 
    
  2. Machen Sie einen neuen generischen Typ mit den entsprechenden Typenargumenten:

    let secondType = genericFooType.MakeGenericType(first.GetType(), typeof<First>) 
    
  3. Erstellen Sie die Instanz des neuen Typs über einen geeigneten Konstruktor. In diesem Fall gibt es einen einzigen Konstruktor ohne Parameter:

    let second = secondType.GetConstructor([||]).Invoke([||]) 
    
    second.GetType() = typeof<Foo<Foo<First,First>, First>> // True 
    

Wenn Sie eine der Typargumente der Art des ursprünglichen Objekts müssen wissen, können sie abgerufen werden, wie folgt:

let originalTypeArguments = first.GetType().GenericTypeArguments 

// Prints [|"First"; "First"|] 
printfn "%A" (originalTypeArguments |> Array.map (fun x -> x.Name))