区分引用的通用值“call”和通用函数调用
给出以下内容:
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
是泛型value 而 g
是一个通用函数:是否可以仅从 fq
和 gq
来判断?由于 F# 可以区分程序集中的 f
和 g
之间的差异,因此我认为我很有可能能够从引用中获取元数据。尽管问题可能是 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
这可以用于匹配 Patterns.Call(_,mi,_)
,其中第二个参数mi
是一个 MemberInfo
实例。然而,有一个问题:它不适用于动态程序集(如 FSI)。
Given the following:
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
the following quotations produce what appear to be identical 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](), [])
Digging around with the debugger, I can't see any way to tell that f
is a generic value whereas g
is a generic function: is it possible to tell this just from fq
and gq
? Since F# can tell the difference between f
and g
across assemblies, I figure there's a good chance I may be able to get at the meta data from the quotation. Though an issue may be that F# appears to in fact compile a version of f
which is a function unit -> string * string
(looking at disassembled code; presumably for interop with other .NET languages), so if the quotation is using the function version, the information may be lost.
Update
From @Tomas's guidance, here's what I've come up with:
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
This can be used matching on Patterns.Call(_,mi,_)
, where the second argument mi
is a MemberInfo
instance. However, there is one issue: it doesn't work for dynamic assemblies (like FSI).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
在幕后,泛型值被编译为泛型方法,因为 .NET 运行时没有任何“泛型字段”(或类似的概念)的概念。我认为 F# 使用资源中的二进制 blob“FSharpSignatureData”中存储的信息来区分这两者。
使用此二进制信息的方法是使用 F# PowerPack 中的 F# 元数据读取器。如果你把你写的两行编译成
test.exe
,那么你会这样写:如果你想使用反射来区分两者,那么你必须找到一些方法来连接元数据信息使用反射
MethodInfo
(这可能可以通过CompiledName
属性来完成)。Under the cover, generic values are compiled as generic methods, because the .NET runtime doesn't have any notion of "generic fields" (or something like that). I think that F# distinguishes between the two using the information stored in the binary blob "FSharpSignatureData" in resources.
The way to work with this binary information is to use F# Metadata Reader from F# PowerPack. If you compile the two lines you write into
test.exe
, then you write this:If you wanted to distinguish between the two using reflection, then you'd have to find some way to connect the metadata information with the reflection
MethodInfo
(which probably could be done viaCompiledName
property).