Вызов метода с Go Reflect по имени и с параметром
Это продолжение вызова функции с помощью 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 ответа
Метод имеет тип 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)
}
}
В коде OP есть несколько ошибок,
- имя функции не было сгенерировано должным образом,
- отраженный экземпляр метода не был должным образом проверен на достоверность,
- Параметр env LoggerInit был указателем, значение было отправлено,
- вызов метода не был выполнен правильно.
Вот исправленная версия ( 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)
}
Вы должны обернуть env
переменная в []reflect.Value
поскольку reflect.Value.Call
требует кусочек reflect.Value
,
args := []reflect.Value{reflect.ValueOf(&env),}
method.Call(args)
Кроме того, некоторые опечатки в вашем коде:
modules[0].Module = "Logger"
Проблема в том, что аргумент передан reflect.Value.Call
должен быть типа reflect.Value
сам. Смотрите подпись от https://golang.org/pkg/reflect/
func (v Value) Call(in []Value) []Value