据我所知(请参阅herehere),在reflect package中没有类型发现机制,该机制希望您已经具有要检查的类型或值的实例。

还有其他方法可以发现正在运行的go程序包中的所有导出类型(尤其是结构)吗?

这是我希望拥有的(但不存在):

import "time"
import "fmt"

func main() {
    var types []reflect.Type
    types = reflect.DiscoverTypes(time)
    fmt.Println(types)
}

最终目标是能够发现满足特定条件的程序包的所有结构,然后能够实例化这些结构的新实例。

顺便说一句,标识类型的注册函数不是是我的用例的有效方法。

不管您是否认为这是一个好主意,这就是为什么我想要这种功能(因为我知道您会问):

我写了一个code generation utility,它可以加载源文件并构建AST以扫描嵌入指定类型的类型。该实用程序的输出是一组基于发现类型的go测试功能。我使用go generate调用此实用程序来创建测试功能,然后运行go test来执行生成的测试功能。每次测试更改(或添加新类型)时,我都必须在重新运行go test之前重新运行go generate。这就是注册功能不是有效选项的原因。我想避免go generate步骤,但这将需要我的实用程序成为正在运行的程序包导入的库。库代码将需要以某种方式在init()期间扫描正在运行的 namespace ,以查找嵌入预期库类型的类型。

最佳答案

(请参阅底部的2019更新)

警告:未经测试且不可靠。每当发布新版本的Go时可能会中断。

通过对Go的运行时进行一些修改,可以获得运行时所知道的所有类型。在您自己的程序包中包含一个小的程序集文件,其中包含:

TEXT yourpackage·typelinks(SB), NOSPLIT, $0-0
    JMP reflect·typelinks(SB)

yourpackage中,声明函数原型(prototype)(无主体):
func typelinks() []*typeDefDummy

除了类型定义:
type typeDefDummy struct {
    _      uintptr           // padding
    _      uint64            // padding
    _      [3]uintptr        // padding
    StrPtr *string
}

然后只需调用类型链接,遍历 slice 并读取每个StrPtr作为名称。找那些以yourpackage开头的人。请注意,如果在不同的路径中有两个名为yourpackage的软件包,则此方法不会明确工作。



是的,假设d*typeDefDummy类型的值(请注意星号,非常重要):
t := reflect.TypeOf(*(*interface{})(unsafe.Pointer(&d)))

现在t是一个reflect.Type值,可用于实例化reflect.Value

编辑:我测试并成功执行了此代码,并具有uploaded it as a gist

调整软件包名称,并根据需要添加路径。

更新2019

自从我最初发布此答案以来,发生了很多变化。这是对如何在2019年使用Go 1.11进行相同操作的简短描述。
$GOPATH/src/tl/tl.go
package tl

import (
    "unsafe"
)

func Typelinks() (sections []unsafe.Pointer, offset [][]int32) {
    return typelinks()
}

func typelinks() (sections []unsafe.Pointer, offset [][]int32)

func Add(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer {
    return add(p, x, whySafe)
}

func add(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer
$GOPATH/src/tl/tl.s
TEXT tl·typelinks(SB), $0-0
    JMP reflect·typelinks(SB)

TEXT tl·add(SB), $0-0
    JMP reflect·add(SB)
main.go
package main

import (
    "fmt"
    "reflect"
    "tl"
    "unsafe"
)

func main() {
    sections, offsets := tl.Typelinks()
    for i, base := range sections {
        for _, offset := range offsets[i] {
            typeAddr := tl.Add(base, uintptr(offset), "")
            typ := reflect.TypeOf(*(*interface{})(unsafe.Pointer(&typeAddr)))
            fmt.Println(typ)
        }
    }
}

骇客入侵!

09-10 03:15
查看更多