F# generic type instanciation from object and not type

时间:2016-05-13 15:34:05

标签: generics f#

Let's say we have the following type which does nothing for the moment

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

We want to instanciate it like this

type First = class end

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

And what I want to do right now is to instanciate the next object to be something like

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

However I can't do first.GetType() to provide it with a type. So I thought of binding the type to a value of type System.Type and use it like the following

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

But it says that the type typing is not defined, instead of taking the type of first. How can I be able to do something like let second = new Foo<first.getType(),First>() ?

Edit: More details here about what I am trying to aim for: let's say we have a protocol of communication on a channel channel. The protocol of communication is defined using a Map like the following :

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

for instance

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

What I want from that is to generate the following functions

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

but such that, as you can see in the Map, receive(a:Bye) cannot be done before send(a:Hello) or there will be an error at compile-time. It should follow the correct sequence. That's the reason why to do that I want to instanciate a type of type receiveType as the return value of the send(a:Hello) function allowing me to use the receive(a:Bye) in the following way :

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

The last thing is that the Map can have more states than these 2, the length depends on the protocol I use. And the whole idea will be implemented within a type provider so that I can provide types and methods that I just described using intelliSense.

Related question: F# generating types in a type extension function within a type provider

1 个答案:

答案 0 :(得分:2)

实例化类型参数与现有对象不同的泛型类型的实例,可以在运行时使用方法GetGenericTypeDefinitionMakeGenericType完成。

所以,举个例子:

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

type First = class end

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

步骤如下:

  1. 获取没有任何类型参数的泛型类型:

    let genericFooType = first.GetType().GetGenericTypeDefinition()
    
  2. 使用适当的类型参数创建一个新的泛型类型:

    let secondType = genericFooType.MakeGenericType(first.GetType(), typeof<First>)
    
  3. 通过适当的构造函数创建新类型的实例。在这种情况下,有一个没有参数的构造函数:

    let second = secondType.GetConstructor([||]).Invoke([||])
    
    second.GetType() = typeof<Foo<Foo<First,First>, First>> // True
    
  4. 如果您需要知道原始对象类型的任何类型参数,可以按如下方式检索它们:

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