就我所知(参见herehere),reflect package中没有类型发现机制,它期望您已经拥有要判断的类型或值的实例.

Is there any other way to discover all exported types (especially the structs) in a running go package?

以下是我希望拥有的东西(但它并不存在):

import "time"
import "fmt"

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

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

顺便说一句,标识类型的注册函数not对于我的用例来说是一个有效的方法.


Whether you think it's a good idea or not, here's why I want this capability (because I know you're going to ask):

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

推荐答案

(2019年更新见底部)

Warning:未经测试,很难理解.每当发布新版本的围棋时都会中断.

通过稍微修改一下Go的运行时,可以获得运行时知道的所有类型.在您自己的软件包中包含一个小程序集文件,该文件包含:

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

yourpackage中,声明函数原型(无体):

func typelinks() []*typeDefDummy

在类型定义旁边:

type typeDefDummy struct {
    _      uintptr           // padding
    _      uint64            // padding
    _      [3]uintptr        // padding
    StrPtr *string           
}

然后只需调用类型链接,遍历切片并读取每个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)
        }
    }
}

快乐黑客!

Go相关问答推荐

T的Golang通用切片,其中 *T实现接口

如何修复Go中调用GetRawInputDeviceInfA Windows API函数时的错误?

GO:如何指定类型约束,使S方法的参数类型与接收方的参数类型相同

如何在Golang中覆盖404

如何使redis池的等待超时

Json.Unmarshal() 和 gin.BindJson() 之间的区别

如何在正则表达式中使整个单词可选?

使用Goldmark在golang中添加ChildNode会导致堆栈溢出

将字符串格式的x509证书生成主题名称

xml.Unmarshal 不支持的类型 struct

golang中如何声明多个接口约束?

如何在 Golang 中使用具有相同名称或特定关键字的行或列重新排列/排序 CSV

Golang 有类似 C++ 的 decltype 的东西吗?

gob 解码器仅返回数组中的第一个元素

如何在 Gorm 中获得特定日期的最大值?

Scanner.Buffer - 最大值对自定义拆分没有影响?

Go 赋值涉及到自定义类型的指针

如何使用 context.WithCancel 启动和停止每个会话的心跳?

在 Go 中表达函数的更好方法( struct 方法)

为什么 Go 不允许将一个泛型分配给另一个泛型?