使用 Go Reflect 按名称和参数调用方法
Calling a method with Go Reflect by name and with a parameter
这是 的后续。
为了简化问题,我删掉了我能删掉的内容,对一些值进行了硬编码,并且~希望~在这个过程中没有让它变得不清楚。我在底部附近的代码 "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.
}
问题是传递给 reflect.Value.Call
的参数本身必须是 reflect.Value
类型。查看来自 https://golang.org/pkg/reflect/#Value.Call
的签名
func (v Value) Call(in []Value) []Value
您必须将 env
变量包装在 []reflect.Value
中,因为 reflect.Value.Call
需要一片 reflect.Value
.
args := []reflect.Value{reflect.ValueOf(&env),}
method.Call(args)
此外,您的代码中有一些拼写错误:
modules[0].Module = "Logger"
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)
}
该方法的类型为 func(*Environment)
。 Assert to that type 并调用:
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)
}
}
这是
为了简化问题,我删掉了我能删掉的内容,对一些值进行了硬编码,并且~希望~在这个过程中没有让它变得不清楚。我在底部附近的代码 "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.
}
问题是传递给 reflect.Value.Call
的参数本身必须是 reflect.Value
类型。查看来自 https://golang.org/pkg/reflect/#Value.Call
func (v Value) Call(in []Value) []Value
您必须将 env
变量包装在 []reflect.Value
中,因为 reflect.Value.Call
需要一片 reflect.Value
.
args := []reflect.Value{reflect.ValueOf(&env),}
method.Call(args)
此外,您的代码中有一些拼写错误:
modules[0].Module = "Logger"
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)
}
该方法的类型为 func(*Environment)
。 Assert to that type 并调用:
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)
}
}