【问题标题】:Get name of function using reflection使用反射获取函数名称
【发布时间】:2012-05-31 08:20:28
【问题描述】:

我正在尝试使用 Go 的反射系统来检索函数的名称,但在调用其类型的 Name 方法时得到一个空字符串。这是预期的行为吗?

这是我如何解决问题的一个简单示例:

package main

import "fmt"
import "reflect"

func main() {
    typ := reflect.TypeOf(main)
    name := typ.Name()
    fmt.Println("Name of function" + name)
}

【问题讨论】:

  • 在我看来 main 的类型是function。你期望的名字是什么?
  • 这很重要。代码示例可能不应该工作,但我认为问题的名称是有效的。

标签: go reflection go-reflect


【解决方案1】:

解决方案是使用FuncForPc,它返回*Func

这会返回"main.main"

package main

import "fmt"
import "reflect"
import "runtime"


func main() {
    name := runtime.FuncForPC(reflect.ValueOf(main).Pointer()).Name()
    fmt.Println("Name of function : " + name)
}

如果你想要"main",只需对其进行标记。

【讨论】:

  • 或者,没有反射,pc, _, _, _ := runtime.Caller(0) fmt.Println("函数名称:" + runtime.FuncForPC(pc).Name())
  • 有趣。但我假设 OP 正在寻找更通用的解决方案,具有任何功能。
【解决方案2】:
package main

import "fmt"
import "runtime"

func main() {
    pc, _, _, _ := runtime.Caller(0)
    fmt.Println("Name of function: " + runtime.FuncForPC(pc).Name())
    fmt.Println()

    // or, define a function for it
    fmt.Println("Name of function: " + funcName())
    x()
}

func funcName() string {
    pc, _, _, _ := runtime.Caller(1)
    return runtime.FuncForPC(pc).Name()
}

func x() {
    fmt.Println("Name of function: " + funcName())
    y()
}

func y() {
    fmt.Println("Name of function: " + funcName())
    z()
}
func z() {
    fmt.Println("Name of function: " + funcName())
}

输出:

函数名称:main.main

函数名称:main.main
函数名称:main.x
函数名称:main.y
函数名:main.z

【讨论】:

  • 就个人而言,我更喜欢您的解决方案(或@Koala3 对您自己的解决方案的扩展),主要是因为它允许将运行时调用封装在一个函数中,然后可以“安全地”部署在其他地方;如果 Go 运行时功能由于某种原因发生了变化,那么整个代码只需要在一个地方进行更改。此外,避免反射是一个巧妙的技巧,干得好! :)
  • 很好的答案索尼娅!一个警告是FuncForPC(pc) 返回一个指针,所以你应该总是检查 nil。我将根据上述核心原则分享一个完整的生产级实用程序函数作为答案。谢谢!
【解决方案3】:
import runtime

func funcName() string {
    pc, _, _, _ := runtime.Caller(1)
    nameFull := runtime.FuncForPC(pc).Name()    // main.foo
    nameEnd := filepath.Ext(nameFull)           // .foo
    name := strings.TrimPrefix(nameEnd, ".")    // foo
    return name
}

【讨论】:

  • 保持包名和函数名的好处当然是,在不同的包中可以有多个同名的函数......然后你不知道哪个是哪个:) 事实上,我在 SO 上搜索这个答案的主要原因是因为我不确定我的哪个函数导致了错误,除了函数名之外,我还需要追踪精确的包名......请注意您还需要导入 stringsfilepath 才能使您的解决方案正常工作!
【解决方案4】:

这是一个经过测试的生产就绪实用程序函数,用于返回函数名称。

注意 1:我们正在处理来自 FuncForPC 的零指针的可能性

注意 2:optFuncLevel 只是 stack frame 级别的友好名称。这使我们可以灵活地在另一层实用函数中使用它。 来自 main 的直接调用只会传递 1(或默认值不传递),但如果我在日志丰富函数中调用 FunctionName,比如从常规代码调用的 PrettyLog(),我会将其称为 @ 987654325@在PrettyLog的调用中,所以返回的函数名是PrettyLog调用者的名字,而不是PrettyLog本身。

// FunctionName returns the function name of the caller
// optFuncLevel passes the function level to go back up.
// The default is 1, referring to the caller of this function
func FunctionName(optFuncLevel ...int) (funcName string) {
    frameLevel := 1 // default to the caller's frame
    if len(optFuncLevel) > 0 {
        frameLevel = optFuncLevel[0]
    }

    if pc, _, _, ok := runtime.Caller(frameLevel); ok {
        fPtr := runtime.FuncForPC(pc)
        if fPtr == nil {
            return
        }
        // Shorten full function name a bit
        farr := strings.SplitN(fPtr.Name(), "/", 2)
        if len(farr) < 2 {
            return
        }
        return farr[1]
    }

    return
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多