英文:
Call a Struct and its Method by name in Go?
问题
我在这里找到了一个函数调用MethodByName()
的链接http://golang.org/pkg/reflect/#Value.MethodByName,但这不完全是我想要的!(可能是因为我不知道如何使用它...我找不到任何使用它的示例)。我想要的是:
type MyStruct struct {
//这里是一些字段
}
func (p *MyStruct) MyMethod {
println("我的语句。");
}
CallFunc("MyStruct", "MyMethod");
//打印出"我的语句。"
所以我猜,首先我需要类似StructByName()
的东西,然后再用它来调用MethodByName()
,对吗?
英文:
I have found a function call MethodByName()
here http://golang.org/pkg/reflect/#Value.MethodByName but it's not exactly what I want! (maybe because I don't know how to use it ... I cannot find any example with it). What I want is:
type MyStruct struct {
//some feilds here
}
func (p *MyStruct) MyMethod {
println("My statement.");
}
CallFunc("MyStruct", "MyMethod");
//print out "My statement."
So I guess, first I need something like StructByName()
and after that use it for MethodByName()
, is that right!?
答案1
得分: 71
要在对象上调用一个方法,首先使用<code>reflect.ValueOf</code>。然后通过名称找到方法,最后调用找到的方法。例如:
package main
import "fmt"
import "reflect"
type T struct {}
func (t *T) Foo() {
fmt.Println("foo")
}
func main() {
var t T
reflect.ValueOf(&t).MethodByName("Foo").Call([]reflect.Value{})
}
英文:
To call a method on an object, first use <code>reflect.ValueOf</code>. Then find the method by name, and then finally call the found method. For example:
package main
import "fmt"
import "reflect"
type T struct {}
func (t *T) Foo() {
fmt.Println("foo")
}
func main() {
var t T
reflect.ValueOf(&t).MethodByName("Foo").Call([]reflect.Value{})
}
答案2
得分: 36
type YourT1 struct {}
func (y YourT1) MethodBar() {
//do something
}
type YourT2 struct {}
func (y YourT2) MethodFoo(i int, oo string) {
//do something
}
func Invoke(any interface{}, name string, args... interface{}) {
inputs := make([]reflect.Value, len(args))
for i, _ := range args {
inputs[i] = reflect.ValueOf(args[i])
}
reflect.ValueOf(any).MethodByName(name).Call(inputs)
}
func main() {
Invoke(YourT2{}, "MethodFoo", 10, "abc")
Invoke(YourT1{}, "MethodBar")
}
英文:
type YourT1 struct {}
func (y YourT1) MethodBar() {
//do something
}
type YourT2 struct {}
func (y YourT2) MethodFoo(i int, oo string) {
//do something
}
func Invoke(any interface{}, name string, args... interface{}) {
inputs := make([]reflect.Value, len(args))
for i, _ := range args {
inputs[i] = reflect.ValueOf(args[i])
}
reflect.ValueOf(any).MethodByName(name).Call(inputs)
}
func main() {
Invoke(YourT2{}, "MethodFoo", 10, "abc")
Invoke(YourT1{}, "MethodBar")
}
Really the code needs to check the method's input number and even whether the method itself exists.
You can reference this http://gowalker.org/reflect#Type
- Check "any" is a struct type
- Check "any" has "name" method
- Check the number of method "name" input parameters is equal the length of args
- Implement
ret
byreflect.Value.Interface()
and be careful the Ptr type;
Or you can use SomeInterface{}
instead of directly using interface{}
to ensure this "any" type, like this:
type Shape interface {
Area() float64 //some method to ensure any is an Shape type.
}
func Invoke(s Shape, name string, inputs...interface{}) []interface{} {
}
so this is OK
color := Invoke(Circle{}, "GetColor")[0].(Color)
but
Invoke(NotAShape{}, "ForBar")
can't be compiled because NotAnShape
isn't a Shape.
If you can't be sure about the first type at compile time, you can build a map to store all possible type, like this:
map[string]reflect.Value{
"YourT1" : reflect.ValueOf(YourT1{})
"YourT2" : reflect.ValueOf(YourT2{})
"Circle" : reflect.ValueOf(Cirlce{}) // or reflect.ValueOf(&Circle{})
}
答案3
得分: 3
// 调用带有错误处理的结构体方法
// Invoke - firstResult, err := Invoke(AnyStructInterface, MethodName, Params...)
func invoke(any interface{}, name string, args ...interface{}) (reflect.Value, error) {
method := reflect.ValueOf(any).MethodByName(name)
methodType := method.Type()
numIn := methodType.NumIn()
if numIn > len(args) {
return reflect.ValueOf(nil), fmt.Errorf("方法 %s 必须至少有 %d 个参数。当前有 %d 个", name, numIn, len(args))
}
if numIn != len(args) && !methodType.IsVariadic() {
return reflect.ValueOf(nil), fmt.Errorf("方法 %s 必须有 %d 个参数。当前有 %d 个", name, numIn, len(args))
}
in := make([]reflect.Value, len(args))
for i := 0; i < len(args); i++ {
var inType reflect.Type
if methodType.IsVariadic() && i >= numIn-1 {
inType = methodType.In(numIn - 1).Elem()
} else {
inType = methodType.In(i)
}
argValue := reflect.ValueOf(args[i])
if !argValue.IsValid() {
return reflect.ValueOf(nil), fmt.Errorf("方法 %s。参数[%d] 必须是 %s 类型。当前是 %s", name, i, inType, argValue.String())
}
argType := argValue.Type()
if argType.ConvertibleTo(inType) {
in[i] = argValue.Convert(inType)
} else {
return reflect.ValueOf(nil), fmt.Errorf("方法 %s。参数[%d] 必须是 %s 类型。当前是 %s", name, i, inType, argType)
}
}
return method.Call(in)[0], nil
}
英文:
gist
invoke struct method with error handling
// Invoke - firstResult, err := Invoke(AnyStructInterface, MethodName, Params...)
func invoke(any interface{}, name string, args ...interface{}) (reflect.Value, error) {
method := reflect.ValueOf(any).MethodByName(name)
methodType := method.Type()
numIn := methodType.NumIn()
if numIn > len(args) {
return reflect.ValueOf(nil), fmt.Errorf("Method %s must have minimum %d params. Have %d", name, numIn, len(args))
}
if numIn != len(args) && !methodType.IsVariadic() {
return reflect.ValueOf(nil), fmt.Errorf("Method %s must have %d params. Have %d", name, numIn, len(args))
}
in := make([]reflect.Value, len(args))
for i := 0; i < len(args); i++ {
var inType reflect.Type
if methodType.IsVariadic() && i >= numIn-1 {
inType = methodType.In(numIn - 1).Elem()
} else {
inType = methodType.In(i)
}
argValue := reflect.ValueOf(args[i])
if !argValue.IsValid() {
return reflect.ValueOf(nil), fmt.Errorf("Method %s. Param[%d] must be %s. Have %s", name, i, inType, argValue.String())
}
argType := argValue.Type()
if argType.ConvertibleTo(inType) {
in[i] = argValue.Convert(inType)
} else {
return reflect.ValueOf(nil), fmt.Errorf("Method %s. Param[%d] must be %s. Have %s", name, i, inType, argType)
}
}
return method.Call(in)[0], nil
}
答案4
得分: 0
package main
import (
"fmt"
"reflect"
)
type Log struct {
Path string
Level string
}
func (l *Log) Conversion(i interface{}) {
if data, ok := i.(*Log); ok {
if data != nil {
if len(data.Path) > 0 {
l.Path = data.Path
}
if len(data.Level) > 0 {
l.Level = data.Level
}
}
}
}
type Storage struct {
Type string
ServerList []string
}
func (s *Storage) Conversion(i interface{}) {
if data, ok := i.(*Storage); ok {
if data != nil {
if len(data.Type) > 0 {
s.Type = data.Type
}
}
}
}
type Server struct {
LogConfig *Log
StorageConfig *Storage
}
func main() {
def := Server{
LogConfig: &Log{
Path: "/your/old/log/path/",
Level: "info",
},
StorageConfig: &Storage{
Type: "zookeeper",
ServerList: []string{"127.0.0.1:2181"},
},
}
fmt.Println(def)
cur := Server{
LogConfig: &Log{
Path: "/your/new/log/path/",
Level: "debug",
},
StorageConfig: &Storage{
Type: "etcd",
ServerList: []string{"127.0.0.1:2379"},
},
}
fmt.Println(cur)
defV := reflect.ValueOf(def)
curV := reflect.ValueOf(cur)
for k := 0; k < defV.NumField(); k++ {
in := make([]reflect.Value, 1)
in[0] = reflect.ValueOf(curV.Field(k).Interface())
defV.Field(k).MethodByName("Conversion").Call(in)
}
fmt.Println(def.LogConfig)
fmt.Println(def.StorageConfig)
}
英文:
package main
import (
"fmt"
"reflect"
)
type Log struct {
Path string
Level string
}
func (l *Log) Conversion(i interface{}) {
if data, ok := i.(*Log); ok {
if data != nil {
if len(data.Path) > 0 {
l.Path = data.Path
}
if len(data.Level) > 0 {
l.Level = data.Level
}
}
}
}
type Storage struct {
Type string
ServerList []string
}
func (s *Storage) Conversion(i interface{}) {
if data, ok := i.(*Storage); ok {
if data != nil {
if len(data.Type) > 0 {
s.Type = data.Type
}
}
}
}
type Server struct {
LogConfig *Log
StorageConfig *Storage
}
func main() {
def := Server{
LogConfig: &Log{
Path: "/your/old/log/path/",
Level: "info",
},
StorageConfig: &Storage{
Type: "zookeeper",
ServerList: []string{"127.0.0.1:2181"},
},
}
fmt.Println(def)
cur := Server{
LogConfig: &Log{
Path: "/your/new/log/path/",
Level: "debug",
},
StorageConfig: &Storage{
Type: "etcd",
ServerList: []string{"127.0.0.1:2379"},
},
}
fmt.Println(cur)
defV := reflect.ValueOf(def)
curV := reflect.ValueOf(cur)
for k := 0; k < defV.NumField(); k++ {
in := make([]reflect.Value, 1)
in[0] = reflect.ValueOf(curV.Field(k).Interface())
defV.Field(k).MethodByName("Conversion").Call(in)
}
fmt.Println(def.LogConfig)
fmt.Println(def.StorageConfig)
}
答案5
得分: 0
package main
import ("fmt";"math/rand";"reflect";"time")
func Invoke(obj any, name string, args ...any) []reflect.Value {
inputs := make([]reflect.Value, len(args))
for i, _ := range args {
inputs[i] = reflect.ValueOf(args[i])
}
return reflect.ValueOf(obj).MethodByName(name).Call(inputs)
}
type Score struct{}
func (s Score) IsExcellent(score int) (bool, error) {
if score < 0 {
return false, fmt.Errorf("invalid score")
}
if score > 90 {
return true, nil
}
return false, nil
}
func (s Score) Generate(min, max int) (int, time.Time) {
rand.Seed(time.Now().UnixNano())
return rand.Intn(max-min) + min, time.Now()
}
func main() {
s := Score{}
values1 := Invoke(s, "IsExcellent", 95)
values2 := Invoke(s, "IsExcellent", -5)
for _, values := range [][]reflect.Value{values1, values2} {
if len(values) > 0 {
err := values[1].Interface()
if err != nil {
fmt.Println(err.(error))
continue
}
fmt.Println(values[0].Bool())
}
}
values := Invoke(Score{}, "Generate", 0, 101)
randNumber := values[0].Int()
createTime := values[1].Interface().(time.Time) // It's better to check `values[1].Interface()` equal to nil first before you do assertion.
fmt.Println(randNumber, createTime)
}
<kbd>go playground</kbd>
英文:
This is great. I added the return value example for it.
package main
import ("fmt";"math/rand";"reflect";"time")
func Invoke(obj any, name string, args ...any) []reflect.Value {
inputs := make([]reflect.Value, len(args))
for i, _ := range args {
inputs[i] = reflect.ValueOf(args[i])
}
return reflect.ValueOf(obj).MethodByName(name).Call(inputs)
}
type Score struct{}
func (s Score) IsExcellent(score int) (bool, error) {
if score < 0 {
return false, fmt.Errorf("invalid score")
}
if score > 90 {
return true, nil
}
return false, nil
}
func (s Score) Generate(min, max int) (int, time.Time) {
rand.Seed(time.Now().UnixNano())
return rand.Intn(max-min) + min, time.Now()
}
func main() {
s := Score{}
values1 := Invoke(s, "IsExcellent", 95)
values2 := Invoke(s, "IsExcellent", -5)
for _, values := range [][]reflect.Value{values1, values2} {
if len(values) > 0 {
err := values[1].Interface()
if err != nil {
fmt.Println(err.(error))
continue
}
fmt.Println(values[0].Bool())
}
}
values := Invoke(Score{}, "Generate", 0, 101)
randNumber := values[0].Int()
createTime := values[1].Interface().(time.Time) // It's better to check `values[1].Interface()` equal to nil first before you do assertion.
fmt.Println(randNumber, createTime)
}
<kbd>go playground</kbd>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论