web-dev-qa-db-fra.com

Appelez une structure et sa méthode par son nom dans Go?

J'ai trouvé un appel de fonction MethodByName() ici http://golang.org/pkg/reflect/#Value.MethodByName mais ce n'est pas exactement ce que je veux! (peut-être parce que je ne sais pas comment l'utiliser ... Je ne trouve aucun exemple avec cela). Ce que je veux c'est:

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

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

Donc, je suppose que, d’abord, j’ai besoin de quelque chose comme StructByName() et que je l’utilise ensuite pour MethodByName(), est-ce exact?

24
nvcnvn

Pour appeler une méthode sur un objet, utilisez d'abord reflect.ValueOf. Recherchez ensuite la méthode par son nom, puis appelez finalement la méthode trouvée. Par exemple:

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{})
}
43
user811773
  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")
 }

Vraiment le code doit vérifier le numéro d’entrée de la méthode ou s’auto-vérifier valablement. Vous pouvez référencer ceci http://gowalker.org/reflect#Type

  1. Vérifier que "tout" est un type de structure
  2. Vérifier la méthode "any" a "name"
  3. Vérifiez que le nombre de paramètres d'entrée "name" de la méthode est égal à la longueur de args
  4. Implémenter ret par reflect.Value.Interface()

et faites attention au type Ptr; ou Vous pouvez utiliser SomeInterface{} au lieu d’utiliser directement interface{} pour vous assurer que ce type "n’importe", comme ceci

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

alors c'est OK

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

mais 

   Invoke(NotAnShape{}, "ForBar") 

ne peut pas être compilé car NotAnShape n'est pas une forme.

Si vous ne savez pas quel premier type sera utilisé lors de la compilation, vous pouvez créer une carte pour stocker tous les types possibles, comme ceci.

    map[string]reflect.Value{
            "YourT1" : reflect.ValueOf(YourT1{})
            "YourT2" : reflect.ValueOf(YourT2{})
            "Circle" : reflect.ValueOf(Cirlce{}) // or reflect.ValueOf(&Circle{})
    }  
18
snyh

Gist Invoque la méthode struct avec traitement des erreurs

// 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
}
1
Ramil Gilfanov
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
Hao