0

这是使用 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 main

import (
  "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

4 回答 4

2

该方法具有类型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)
    }
}

在操场上运行它

于 2018-10-27T10:55:32.840 回答
1

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)
}
于 2018-10-27T10:36:06.710 回答
0

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

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

此外,您的代码中有一些拼写错误:

modules[0].Module = "Logger"
于 2018-10-27T10:24:51.620 回答
0

问题是传递的参数reflect.Value.Call必须是类型reflect.Value本身。查看来自https://golang.org/pkg/reflect/#Value.Call的签名

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

于 2018-10-27T10:08:23.980 回答