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

发布于 2024-10-21 11:51:28 字数 1494 浏览 2 评论 0原文

给出以下内容:

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

以下引用产生看起来相同的 Exprs:

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 是一个通用函数:是否可以仅从 fqgq 来判断?由于 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

这可以用于匹配 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 Exprs:

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

两个我 2024-10-28 11:51:28

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

使用此二进制信息的方法是使用 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 属性来完成)。

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:

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

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 via CompiledName property).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文