Call a Struct and its Method by name in Go?

谁都会走 提交于 2019-11-27 10:28:19

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{})
}
  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 code need check the method's input number or method it self whether validly . You can reference this http://gowalker.org/reflect#Type

  1. Check "any" is an 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 use 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(NotAnShape{}, "ForBar") 

can't be compiled because NotAnShape isn't a Shape.

If you can't sure which the first type will be used at compile time, you can build an 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{})
    }  

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
}
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)

}

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!