如何在Go中以字符串形式获取函数的签名

时间:2019-01-10 12:45:08

标签: go reflection

我正在实现一个可以加载go插件的go模块。

我假设主程序包中存在具有特定名称和特定签名的函数,并且如果找不到或与期望的签名不匹配,希望有一个不错的错误消息。

给出一个具有函数类型的变量,如何获得该函数的基础签名?

以下仅显示类型名称(例如main.ModuleInitFunc),而不显示完整签名。

package main

import "fmt"

type ModuleInitFunc func(someInt int) error

func main() {
    var myFunc ModuleInitFunc = nil

    fmt.Printf("%T", lol)
}

1 个答案:

答案 0 :(得分:3)

reflect.Type.String()仅返回类型名称,因此,如果函数值具有命名类型,则只会看到类型名称。请注意,如果函数值是函数文字(具有未命名的类型),则会打印函数的签名:

var myFunc ModuleInitFunc

fmt.Printf("%T\n", myFunc)
fmt.Printf("%T\n", func(i int) error { return nil })

输出(在Go Playground上尝试):

main.ModuleInitFunc
func(int) error

如果类型是命名类型,我们必须自己构造签名,但是幸运的是reflect.Type拥有我们所需的全部信息。

Type.In()返回第i th 参数的类型,类似地,Type.Out()返回第i 结果类型的类型。

使用这些示例,下面是一个示例实现,该示例返回函数值的签名:

func signature(f interface{}) string {
    t := reflect.TypeOf(f)
    if t.Kind() != reflect.Func {
        return "<not a function>"
    }

    buf := strings.Builder{}
    buf.WriteString("func (")
    for i := 0; i < t.NumIn(); i++ {
        if i > 0 {
            buf.WriteString(", ")
        }
        buf.WriteString(t.In(i).String())
    }
    buf.WriteString(")")
    if numOut := t.NumOut(); numOut > 0 {
        if numOut > 1 {
            buf.WriteString(" (")
        } else {
            buf.WriteString(" ")
        }
        for i := 0; i < t.NumOut(); i++ {
            if i > 0 {
                buf.WriteString(", ")
            }
            buf.WriteString(t.Out(i).String())
        }
        if numOut > 1 {
            buf.WriteString(")")
        }
    }

    return buf.String()
}

测试:

var myFunc ModuleInitFunc

fmt.Println(signature(func(i int) error { return nil }))
fmt.Println(signature(myFunc))
fmt.Println(signature(time.Now))
fmt.Println(signature(os.Open))
fmt.Println(signature(log.New))
fmt.Println(signature(""))

输出(在Go Playground上尝试):

func (int) error
func (int) error
func () time.Time
func (string) (*os.File, error)
func (io.Writer, string, int) *log.Logger
<not a function>

请注意,由于无法存储/访问参数,因此也无法打印参数名称和结果类型。有关详细信息,请参见Is unnamed arguments a thing in Go?