区分引用的通用值“call”和泛型函数调用

时间:2011-03-12 17:57:11

标签: f# quotations

鉴于以下内容:

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是一个通用函数:是否可以从{{1 }和fq?由于F#可以告诉程序集中gqf之间的区别,我认为我很有可能从引文中获取元数据。虽然问题可能是F#似乎实际上编译了g的一个版本f(查看反汇编代码;可能是与其他.NET语言互操作),所以如果报价是使用功能版本,信息可能会丢失。

更新

从@ Tomas的指导下,这就是我的想法:

unit -> string * string

这可用于匹配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 ,其中第二个参数Patterns.Call(_,mi,_)mi个实例。但是,有一个问题:它不适用于动态程序集(如FSI)。

1 个答案:

答案 0 :(得分:3)

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

使用此二进制信息的方法是使用F# PowerPack中的F#元数据读取器。如果你编写你写入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完成属性)。