在Go中按名称调用Struct及其方法?

时间:2011-11-12 09:26:26

标签: go go-reflect

我在http://golang.org/pkg/reflect/#Value.MethodByName找到了一个函数调用MethodByName(),但这并不是我想要的! (也许是因为我不知道如何使用它...我找不到它的任何例子)。我想要的是:

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

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

所以我想,首先我需要像StructByName()之类的东西,然后将其用于MethodByName(),是吧!

4 个答案:

答案 0 :(得分:46)

要在对象上调用方法,请先使用reflect.ValueOf。然后按名称查找方法,最后调用found方法。例如:

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

答案 1 :(得分:19)

  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. 选中“any”是结构类型
  2. 选中“any”具有“名称”方法
  3. 检查方法“name”输入参数的数量是否等于args的长度
  4. ret
  5. 实施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不是形状。

    如果您无法确定在编译时将使用哪种第一种类型,您可以构建一个映射来存储所有可能的类型,如下所示。

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

答案 2 :(得分:1)

gist 调用带有错误处理的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
}

答案 3 :(得分:0)

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)

}