42

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!?

4
  • since MyMethod is a method of *MyStruct, I believe you would at least need an instance of *MyStruct to cal MyMethod with. Maybe it's assumed that CallFunc creates a zeroed instaces of MyStruct? Commented Nov 12, 2011 at 20:52
  • the hard thing is I don't know the type of the Struct yet! Commented Nov 13, 2011 at 2:44
  • Here if you are expecting the ` CallFunc("MyStruct", "MyMethod"); ` to instanciate the struct and execute the function which need to be an impure function to make sense then only reason you have the * there is to not have a pass by value of huge struct.So in essence the struct would work just as a namespace Commented Jul 1, 2016 at 12:34
  • I have posted a number of helpful examples on using reflection. Commented Sep 24, 2019 at 22:45

5 Answers 5

73

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{})
}
Sign up to request clarification or add additional context in comments.

5 Comments

The issue in my case Is I cant not declare t is typed T, its must be some how I can declare t typed T by the name of T is string "T".
@nvcnvn: I would suggest to match the name against the string "T" somewhere in your code and create a value of type T if the name matched. If it matches some other type "U", create a value of type U. The values can be freely passed around as interface{}.
Thank you @Atom, what if the method Foo has any arguments? And has any return values?
``` func callMethod(methodName string, reflectValue reflect.Value) { // Only get address from non-pointer if reflectValue.CanAddr() && reflectValue.Kind() != reflect.Ptr { reflectValue = reflectValue.Addr() } if methodValue := reflectValue.MethodByName(methodName);methodValue.IsValid() { switch method := methodValue.Interface().(type) { case func(): method() case func() error: log.LogError(method()) .............. } } ```
this just outputs [<float64 Value>] rather than the value returned by the function for me
36
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 the code needs to check the method's input number and even whether the method itself exists.

You can reference this http://gowalker.org/reflect#Type

  1. Check "any" is a 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 using 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(NotAShape{}, "ForBar")

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

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

4 Comments

thank you so much for the code. How about the method has any return values?
the return values is more bother becaus you have to manually convert the reflect.Value to your excepted value type. gowalker.org/reflect#Value_Call will return an array of reflect.Values as return values. reflect.Value has the method Interface which will return the an interface.
snyh, can you explain it more? I'll be very grateful if you explain how can i convert []reflect.Value here to error, because my function has this signature func doSmth() error {...}
@vodolaz095 gowalker.org/reflect#Value_Interface err := fmt.Errorf("a error example") vv := reflect.ValueOf(err) s, ok := vv.Interface().(error) if ok { fmt.Println(s) } `
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
}

Comments

1

This is great. I added the return value example for it.

package main
import ("fmt";"math/rand";"reflect";"time")

// Invoke call the struct method and store the return values in a []reflect.Value
// return value example
// assume return value: (int, time.Time, error)
// var r []reflect.Value
// => r[0].interface() // for int
// => r[1].interface() // for time.Time
// => r[2].interface() // for error
// and so on.
func Invoke(obj any, name string, args ...any) []reflect.Value {
    inputs := make([]reflect.Value, len(args))
    for i, _ := range args {
        inputs[i] = reflect.ValueOf(args[i])
    }
    return reflect.ValueOf(obj).MethodByName(name).Call(inputs)
}

type Score struct{}

func (s Score) IsExcellent(score int) (bool, error) {
    if score < 0 {
        return false, fmt.Errorf("invalid score")
    }
    if score > 90 {
        return true, nil
    }
    return false, nil
}

func (s Score) Generate(min, max int) (int, time.Time) {
    rand.Seed(time.Now().UnixNano())
    return rand.Intn(max-min) + min, time.Now()
}

func main() {
    s := Score{}
    values1 := Invoke(s, "IsExcellent", 95)
    values2 := Invoke(s, "IsExcellent", -5)
    for _, values := range [][]reflect.Value{values1, values2} {
        if len(values) > 0 {
            err := values[1].Interface()
            if err != nil {
                fmt.Println(err.(error))
                continue
            }
            fmt.Println(values[0].Bool())
        }
    }
    values := Invoke(Score{}, "Generate", 0, 101)
    randNumber := values[0].Int()
    createTime := values[1].Interface().(time.Time) // It's better to check `values[1].Interface()` equal to nil first before you do assertion.
    fmt.Println(randNumber, createTime)
}

go playground

Comments

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.