2

鉴于以下情况:

let f<'a,'b> = typeof<'a>.Name, typeof<'b>.Name //val f<'a,'b> : string * string
let g<'a,'b>() = typeof<'a>.Name, typeof<'b>.Name //val g<'a,'b> : unit -> string * string

以下引文产生了看似相同Expr的 s:

let fq = <@ f<int,string> @> //Call (None, System.Tuple`2[System.String,System.String] f[Int32,String](), [])
let gq = <@ g<int,string>() @> //Call (None, System.Tuple`2[System.String,System.String] g[Int32,String](), [])

使用调试器进行挖掘,我看不出有任何方法可以判断这f是一个泛型值,而g它是一个泛型函数:是否可以仅从fqand来判断它gq?由于 F# 可以区分程序集之间fg程序集之间的差异,我认为我很有可能能够从引用中获取元数据。虽然一个问题可能是 F# 似乎实际上编译了f一个函数版本unit -> string * string(查看反汇编代码;可能是为了与其他 .NET 语言互操作),所以如果引用使用的是函数版本,则信息可能会丢失.

更新

根据@Tomas 的指导,这是我想出的:

let isGenericValue (mi:MemberInfo) =
    try
        let mOrV =
            FSharpEntity.FromType(mi.DeclaringType).MembersOrValues
            |> Seq.find (fun mOrV -> mOrV.CompiledName = mi.Name)

        not mOrV.Type.IsFunction
    with
    | :? System.NotSupportedException -> true //for dynamic assemblies, just assume idiomatic generic value

这可以用于匹配 on Patterns.Call(_,mi,_),其中第二个参数mi是一个MemberInfo实例。但是,有一个问题:它不适用于动态程序集(如 FSI)。

4

1 回答 1

3

在幕后,泛型值被编译为泛型方法,因为 .NET 运行时没有任何“泛型字段”(或类似的东西)的概念。我认为 F# 使用存储在资源中的二进制 blob“FSharpSignatureData”中的信息来区分两者。

使用此二进制信息的方法是使用 F # PowerPack中的 F# Metadata Reader 。如果你编译你写入的两行test.exe,那么你写这个:

let met = Microsoft.FSharp.Metadata.FSharpAssembly.FromFile(@"C:\temp\test.exe")
for e in met.Entities do
  // Prints 'Test' for the top-level module
  printfn "%A" e.DisplayName 
  for e in e.MembersOrValues do
    // Prints:
    //  "f" false (for value 'f')
    //  "g" true  (for function 'g')
    printfn "- %A %A" e.DisplayName e.Type.IsFunction

如果您想使用反射区分两者,那么您必须找到某种方法将元数据信息与反射联系起来MethodInfo(这可能可以通过CompiledName属性完成)。

于 2011-03-12T18:05:27.650 回答