Называть Структуру и ее Метод по имени в Go? - PullRequest
26 голосов
/ 12 ноября 2011

Я нашел вызов функции MethodByName() здесь http://golang.org/pkg/reflect/#Value.MethodByName но это не совсем то, что я хочу!(возможно, потому что я не знаю, как использовать это ... я не могу найти никакого примера с этим).То, что я хочу, это:

type MyStruct struct {
//some feilds here
} 
func (p *MyStruct) MyMethod { 
    println("My statement."); 
} 

CallFunc("MyStruct", "MyMethod"); 
//print out My statement." 

Так что, я думаю, сначала мне нужно что-то вроде StructByName(), а после этого использовать его для MethodByName(), верно?

Ответы [ 4 ]

46 голосов
/ 12 ноября 2011

Чтобы вызвать метод объекта, сначала используйте reflect.ValueOf.Затем найдите метод по имени и, наконец, вызовите найденный метод.Например:

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{})
}
19 голосов
/ 01 ноября 2013
  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")
 }

Действительно, код должен проверить правильность введенного номера метода или метода. Вы можете сослаться на это http://gowalker.org/reflect#Type

  1. Проверьте, что "любой" является типом структуры
  2. Проверьте, что "любой" имеет метод "name"
  3. Проверка количества входных параметров метода "имя" равна длине аргументов
  4. Реализация ret по reflect.Value.Interface()

и будьте осторожны с типом Ptr; или Вы можете использовать SomeInterface{} вместо непосредственного использования interface{}, чтобы обеспечить этот «любой» тип, такой как

   type Shape interface {
       Area() float64  //some method to ensure any is an Shape type.
   }
           func Invoke(s Shape, name string, inputs...interface{}) []interface{} {
   }

так что все в порядке

   color := Invoke(Circle{}, "GetColor")[0].(Color)

но

   Invoke(NotAnShape{}, "ForBar") 

не может быть скомпилировано, потому что NotAnShape не является Shape.

Если вы не уверены, какой первый тип будет использоваться во время компиляции, вы можете построить карту для хранения всех возможных типов, как этот.

    map[string]reflect.Value{
            "YourT1" : reflect.ValueOf(YourT1{})
            "YourT2" : reflect.ValueOf(YourT2{})
            "Circle" : reflect.ValueOf(Cirlce{}) // or reflect.ValueOf(&Circle{})
    }  
1 голос
/ 14 ноября 2018

суть вызвать метод struct с обработкой ошибок

// 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 голосов
/ 08 января 2019
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)

}

...