【问题标题】:Calling a function with Go Reflect使用 Go Reflect 调用函数
【发布时间】:2019-03-30 16:12:58
【问题描述】:

我想知道是否有可能不知道函数名但无论如何调用它并从中获取值。这将我带到了反射包,我非常接近,但我不确定最后一步 - 如果有的话。如果我遗漏了一些明显的东西,请再次原谅我,这是我第一次尝试在 Go 中做任何事情,而不是设置它。

当然,作为一种编译语言,没有必要遍历事物来查找函数名称,我都知道,但这是我想看看是否可能的东西……我正在玩耍和学习。

下面是代码。我真正想做的是在主行中提取 ModuleBoot() 和 SomethingBoot() 中设置的值,但目前我能得到的只是结构信息。也许事情就是这样,但也许有一个步骤或改变可以让它进入下一步。

希望我正确复制了所有相关代码,以便按原样编译:

// Using: go version go1.9.7 linux/amd64
=======================================
FILE: main.go
=======================================
package main

import (
  "fmt"
  "reflect"
  "playing/modules/core"
)

func main() {

  miType := reflect.TypeOf(core.ModuleInfo{})

  fmt.Println("")

  for i := 0; i < miType.NumMethod(); i++ {
    method := miType.Method(i)
    fmt.Println(method.Name)

    in := make([]reflect.Value, method.Type.NumIn())
    in[0] = reflect.ValueOf(core.ModuleInfo{})
    //fmt.Println("Params in:", method.Type.NumIn(), "Params out:", method.Type.NumOut())

    mi := method.Func.Call(in)
    fmt.Println("mi:", mi)

    fmt.Println("")
  }
}

=======================================
FILE: playing/modules/core/something.go
=======================================
package core

func (mi ModuleInfo) SomethingBoot() ModuleInfo {
  mi.Version = "1.0000"
  mi.Priority = 10
  return mi
}

=======================================
FILE: playing/modules/core/modules.go
=======================================
package core

type ModuleInfo struct {
  Version string
  Priority int
}

func (mi ModuleInfo) ModuleBoot() ModuleInfo {
  mi.Version = "1.0012"
  mi.Priority = 23
  return mi
}

我从中得到的输出是:

Started delve with config "Debug"

SomethingBoot
mi: [<core.ModuleInfo Value>]

ModuleBoot
mi: [<core.ModuleInfo Value>]

delve closed with code 0

【问题讨论】:

    标签: go reflect


    【解决方案1】:

    要以 ModuleInfo 的形式获取返回值,请获取第一个返回值的 underlying value 和 ModuleInfo 的接口值 type assert

    // mi has type core.ModuleInfo
    mi := method.Func.Call(in)[0].Interface().(core.ModuleInfo)
    

    Run it on the Playground.

    您可以通过将方法声明到具有正确签名的函数并直接调用该函数来削减一些反射代码:

    for i := 0; i < miType.NumMethod(); i++ {
        method := miType.Method(i).Func.Interface().(func(core.ModuleInfo) core.ModuleInfo)
        mi := method(core.ModuleInfo{})
        fmt.Println("Version", mi.Version)
        fmt.Println("Priority", mi.Priority)
        fmt.Println("")
    }
    

    Run it on the Playground

    【讨论】:

    • 太棒了,谢谢!我在四处寻找任何正在研究这个的人时也遇到了这个很棒的资源:@​​987654325@
    • 在没有反射的第二个示例中,当 ModuleInfo 是导入的包“核心”时,我无法让它工作(参见上面的代码)。我得到: ./main.go:42:65: undefined: ModuleInfo ./main.go:43:16: undefined: ModuleInfo 并且当我尝试像这样引用模块时:method := miType.Method(i)。 Func.Interface().(func(core.ModuleInfo) core.ModuleInfo) 我得到编译错误: ./main.go:43:16: undefined: ModuleInfo ./main.go:45:29: mi.Priority undefined (类型 core.ModuleInfo 没有字段或方法优先级)。
    • 我修正了错字。
    • 你真的很懂你的围棋,谢谢!我被困在最后一个问题上,希望您能提供帮助,但我认为在这里发帖是不对的,所以我在stackoverflow.com/questions/53020517/… 提出了一个单独的问题。我必须承认我仍在尝试消化您所做的那一行:“method := miType.Method(i).Func.Interface().(func(core.ModuleInfo) core.ModuleInfo)”,因为我不认为我还完全明白。我一直在将它与文档进行匹配。我最终会得到它。
    【解决方案2】:

    Go 原生支持将函数作为值;你不需要反思来做到这一点。

    特别是,如果您将两个函数设为顶级函数(不专门绑定到结构):

    package core
    type ModuleInfo struct { ... }
    func SomethingBoot() ModuleInfo
    func ModuleBoot() ModuleInfo
    

    然后你可以写一个函数,以函数为参数:

    func PrintVersion(func booter() core.ModuleInfo) {
            mi := booter()
            fmt.Printf("version %s\n", mi.Version)
    }
    

    您可以将预先存在的函数作为参数传递:

    PrintVersion(core.SomethingBoot)
    PrintVersion(core.ModuleBoot)
    

    请注意,函数名后面没有括号:您将函数本身作为参数传递,而不是调用函数并传递其返回值。

    【讨论】:

    • 感谢您的回复,但您在调用中对函数名称进行了硬编码,例如:PrintVersion(core.SomethingBoot) 这不是我要问的。
    猜你喜欢
    • 2019-03-31
    • 1970-01-01
    • 1970-01-01
    • 2011-09-01
    • 1970-01-01
    • 2019-09-18
    • 1970-01-01
    • 2022-10-15
    • 2017-01-29
    相关资源
    最近更新 更多