Call a Struct and its Method by name in Go?

2019-01-10 04:52发布

问题:

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:

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


回答2:

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


回答3:

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:

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)

}



标签: go go-reflect