何时使用F#的typedefof <'T>与typeof <'T>?

Wal*_*lly 13 f#

有人可以澄清何时使用typedefof <'T>typeof <'T>

双方typedefof<System.String>typeof<System.String>返回相同的Type实例.

但是,它们返回不同的实例和不同的信息System.Collections.Generic.List<_>.

我能想到typedefof一个新的和改进的typeof吗?我应该切换到始终使用typedefof?还是比它更微妙?

Aar*_*ach 11

typeof当您想要获取System.Type给定类型的对象时使用. typedefof当您想要获取System.Type表示泛型类型的类型定义时使用.作为使用两者的示例,假设您有一个名为的类型Generic<'a>,并且您想要创建一个函数,该函数返回任何给定类型的System.Type对象Generic.

type Generic<'a> = Value of 'a

let makeGenericOf<'a> () = 
    typedefof<Generic<_>>.MakeGenericType(typeof<'a>)
Run Code Online (Sandbox Code Playgroud)

在这里,您将使用该typedefof函数来获取类型定义,并typeof获取'a用于构造泛型的类型Generic<'a> Type.


pho*_*oog 9

这应该说明不同之处.使用时typeof,编译器会推断类型参数并构造具体类型.在这种情况下,推断的类型参数是System.Object:

let t1 = typeof<System.Collections.Generic.List<_>>
let t2 = typedefof<System.Collections.Generic.List<_>>

printfn "t1 is %s" t1.FullName
printfn "t2 is %s" t2.FullName
Run Code Online (Sandbox Code Playgroud)

输出:

t1 is System.Collections.Generic.List`1[[System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
t2 is System.Collections.Generic.List`1
Run Code Online (Sandbox Code Playgroud)

因为typeof只能返回构造类型,typedefof所以如果需要表示泛型类型定义的类型对象,则必须这样做.


Wal*_*lly 7

我非常感谢 phoog、Aaron 和 JLRishe 的回答。以下是我根据他们的答案和我自己的实验所了解到的内容。

有两个Type与泛型相关的实例。

  1. 有一个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]"
    
    Run Code Online (Sandbox Code Playgroud)
  2. 有一个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]" 
    
    Run Code Online (Sandbox Code Playgroud)

顺便说一句,该类Type有一个实例方法GetGenericTypeDefinition()。这意味着,以下两个返回相同的实例:

    > Object.ReferenceEquals(typeof<List<int>>.GetGenericTypeDefinition(), typedefof<List<int>>);;
    val it : bool = true        
Run Code Online (Sandbox Code Playgroud)

如果你打电话会发生什么typeof<List<_>>?正如 phoog 提到的,您将得到 的Type定义。List<Object>

> typeof<List<_>>.ToString();;
val it : string = "Microsoft.FSharp.Collections.FSharpList`1[System.Object]"
Run Code Online (Sandbox Code Playgroud)

这一切都有助于理解。例如,假设我需要知道一个对象是否是通用列表(任何类型)。

// 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
Run Code Online (Sandbox Code Playgroud)

另外,如果你想后期绑定实例化泛型类型,你可以使用MakeGenericType(...)Aaron 提到的方法。

> let myList = typedefof<List<_>>.MakeGenericType(typeof<int>);;
val myList : Type = Microsoft.FSharp.Collections.FSharpList`1[System.Int32]
Run Code Online (Sandbox Code Playgroud)