为了账号安全,请及时绑定邮箱和手机立即绑定

使用 Go Reflect 按名称和参数调用方法

使用 Go Reflect 按名称和参数调用方法

Go
慕尼黑8549860 2023-05-04 16:39:43
这是Calling a function with Go Reflect的后续。为了简化问题,我删掉了我能删掉的内容,对一些值进行了硬编码,并且~希望~在这个过程中没有让它变得不清楚。我在底部附近的代码“method.Call(env)”上遇到错误。理想情况下,我想做的是尽量减少反射的使用,就像 ThunderCat 在上一个问题中所做的那样:method := miType.Method(i).Func.Interface().(func(core.ModuleInfo) core.ModuleInfo)但如果那不可能,那么最简单的方法就可以了。如果这看起来像是一个基本问题,我很抱歉,我是 Go 的新手。我得到的错误是:cannot use env (type Environment) as type []reflect.Value in argument to method.Call这是因为我想用正确的签名断言函数的方法,就像在上一个问题中所做的那样,但是经过相当多的尝试之后,我还是不太明白。简化代码:package mainimport (  "flag"  "fmt"  "reflect")type CommandLineFlags struct {  Debug *bool}type Environment struct {  CLF CommandLineFlags}type ModuleInfo struct {  Initialize bool   // Flag: True of module has Initialization function and it should be called. Default: false  Module     string // Name of the module. No need to hard code, will be set during initialization.}type ModuleInit struct{}func main() {  var env Environment  env.CLF.Debug = flag.Bool("dbg", false, "Enables Debug Messages")  flag.Parse()  modules := make([]ModuleInfo, 1)  modules[0].Initialize = true  modules[0].Module = "logger"  miValue := reflect.ValueOf(ModuleInit{})  // miType := reflect.TypeOf(ModuleInit{})  for _, m := range modules {    if m.Initialize {      funcName := m.Module + "Init"      method := miValue.MethodByName(funcName)      fmt.Println(funcName)      // Would like to do something like this      //    ...Func.Interface().(func(core.ModuleInit) core.ModuleInit)      // like is done with the referenced quesiton above so as to minimize the use of reflect calls.      method.Call(env)    }  }}func (mi ModuleInit) LoggerInit(env *Environment) {  var debugEnabled = *env.CLF.Debug  // ...and more stuff.}
查看完整描述

4 回答

?
白板的微信

TA贡献1883条经验 获得超3个赞

该方法的类型为func(*Environment)。 断言该类型并调用:

modules := make([]ModuleInfo, 1)

modules[0].Initialize = true

modules[0].Module = "Logger"


miValue := reflect.ValueOf(ModuleInit{})

for _, m := range modules {

    if m.Initialize {

        funcName := m.Module + "Init"

        method := miValue.MethodByName(funcName).Interface().(func(*Environment))

        method(&env)

    }

}

(注意两个已解决的问题:模块应该是"Logger",而不是"logger",方法需要一个*Environment,而不是Environment。)

如果找不到该方法或没有正确的类型,上面的代码将崩溃。这是带有检查以防止恐慌的代码:

modules := make([]ModuleInfo, 1)

modules[0].Initialize = true

modules[0].Module = "Logger"


miValue := reflect.ValueOf(ModuleInit{})

for _, m := range modules {

    if m.Initialize {

        funcName := m.Module + "Init"

        method := miValue.MethodByName(funcName)

        if !method.IsValid() {

            fmt.Printf("method %s not found", funcName)

            continue

        }

        fn, ok := method.Interface().(func(*Environment))

        if !ok {

            fmt.Println("method is not func(*Environment)")

            continue

        }

        fn(&env)

    }

}


查看完整回答
反对 回复 2023-05-04
?
喵喔喔

TA贡献1735条经验 获得超5个赞

OP代码中有几个错误,

  • 函数名称未正确生成,

  • 反映的方法实例没有正确检查有效性,

  • LoggerInit 的 env 参数是一个指针,传入了一个值,

  • 方法调用未正确完成。

这是固定版本 ( https://play.golang.org/p/FIEc6bTvGWJ )。

package main


import (

    "flag"

    "fmt"

    "log"

    "reflect"

    "strings"

)


type CommandLineFlags struct {

    Debug *bool

}


type Environment struct {

    CLF CommandLineFlags

}


type ModuleInfo struct {

    Initialize bool   // Flag: True of module has Initialization function and it should be called. Default: false

    Module     string // Name of the module. No need to hard code, will be set during initialization.

}


type ModuleInit struct{}


func main() {

    var env Environment


    env.CLF.Debug = flag.Bool("dbg", false, "Enables Debug Messages")

    flag.Parse()


    modules := make([]ModuleInfo, 1)

    modules[0].Initialize = true

    modules[0].Module = "logger"


    miValue := reflect.ValueOf(ModuleInit{})

    // miType := reflect.TypeOf(ModuleInit{})

    for _, m := range modules {

        if m.Initialize {

            funcName := strings.Title(m.Module) + "Init"

            method := miValue.MethodByName(funcName)

            log.Printf("%#v %v\n", method, funcName)

            if !method.IsValid() || method.IsNil() {

                break

            }

            fmt.Println(funcName)

            // Would like to do something like this

            //    ...Func.Interface().(func(core.ModuleInit) core.ModuleInit)

            // like is done with the referenced quesiton above so as to minimize the use of reflect calls.

            out := method.Call([]reflect.Value{reflect.ValueOf(env)})

            fmt.Println(out) // A bunch of relfect.Values.

        }

    }

}


func (mi ModuleInit) LoggerInit(env Environment) {

    var debugEnabled = *env.CLF.Debug

    // ...and more stuff.

    log.Println("LoggerInit ", debugEnabled)

}


查看完整回答
反对 回复 2023-05-04
?
四季花海

TA贡献1811条经验 获得超5个赞

问题是传递的参数本身reflect.Value.Call必须是类型reflect.Value

func (v Value) Call(in []Value) []Value


查看完整回答
反对 回复 2023-05-04
?
慕姐8265434

TA贡献1813条经验 获得超2个赞

您必须将env变量包装在 a 中[]reflect.Value,因为reflect.Value.Call需要一片reflect.Value.

args := []reflect.Value{reflect.ValueOf(&env),}
method.Call(args)

另外,您的代码中有一些错别字:

modules[0].Module = "Logger"


查看完整回答
反对 回复 2023-05-04
  • 4 回答
  • 0 关注
  • 126 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信