鉴于以下内容:
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#可以告诉程序集中gq
和f
之间的区别,我认为我很有可能从引文中获取元数据。虽然问题可能是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)。
答案 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
完成属性)。