Я действительно ценю ответы Фога, Аарона и Дж.Л. Рише.Вот то, что я узнал, основываясь на их ответах и моих собственных экспериментах.
Есть два Type
экземпляра, связанных с генериками.
Есть Type
связано с универсальным, который имеет определенные параметры типа.Например, Type
связан с List<int>
, а другой Type
связан с List<string>
.Это то, что вы получаете, когда используете typeof<>
.
> typeof<List<string>>.ToString();;
val it : string = "Microsoft.FSharp.Collections.FSharpList`1[System.String]"
> typeof<List<int>>.ToString();;
val it : string = "Microsoft.FSharp.Collections.FSharpList`1[System.Int32]"
С самим определением универсального типа связан Type
.Например, существует один Type
, связанный с List<'T>
, который одинаков для List<int>
, List<string>
и List<_>
.Это то, что вы получаете, когда используете typedefof<>
.
> typedefof<List<string>>.ToString();;
val it : string = "Microsoft.FSharp.Collections.FSharpList`1[T]"
> typedefof<List<int>>.ToString();;
val it : string = "Microsoft.FSharp.Collections.FSharpList`1[T]"
> typedefof<List<_>>.ToString();;
val it : string = "Microsoft.FSharp.Collections.FSharpList`1[T]"
Кстати, класс Type
имеет метод экземпляра для GetGenericTypeDefinition()
.Это означает, что следующие два возвращают один и тот же экземпляр:
> Object.ReferenceEquals(typeof<List<int>>.GetGenericTypeDefinition(), typedefof<List<int>>);;
val it : bool = true
Что произойдет, если вы позвоните typeof<List<_>>
?Вы вернули определение Type
для List<Object>
, как упоминал Фог.
> typeof<List<_>>.ToString();;
val it : string = "Microsoft.FSharp.Collections.FSharpList`1[System.Object]"
Это все полезно понять.Например, предположим, мне нужно знать, является ли объект универсальным списком (любого типа).
// does not give me the answer I naively expected
> o.GetType() = typeof<List<_>>;;
val it : bool = false
// does this reference point to a List<'T>?
> o.GetType().IsGenericType && o.GetType().GetGenericTypeDefinition() = typedefof<List<_>>;;
val it : bool = true
Кроме того, если вы хотите создать экземпляр универсального типа с поздним связыванием, вы можете использовать MakeGenericType(...)
метод, о котором говорил Аарон.
> let myList = typedefof<List<_>>.MakeGenericType(typeof<int>);;
val myList : Type = Microsoft.FSharp.Collections.FSharpList`1[System.Int32]