Call a Struct and its Method by name in Go?

后端 未结 4 679
我在风中等你
我在风中等你 2020-12-02 05:47

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 kn

相关标签:
4条回答
  • 2020-12-02 06:00
    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)
    }
    
    0 讨论(0)
  • 2020-12-02 06:14
    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

    1. Check "any" is a struct type
    2. Check "any" has "name" method
    3. Check the number of method "name" input parameters is equal the length of args
    4. Implement ret by reflect.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{})
    }  
    
    0 讨论(0)
  • 2020-12-02 06:20

    To call a method on an object, first use reflect.ValueOf. 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{})
    }
    
    0 讨论(0)
  • 2020-12-02 06:24

    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
    }
    
    0 讨论(0)
提交回复
热议问题