MyStruct. GetSomeAdditionalData() is called method expression and you must provide first argument of type MyStruct to that call. Argument can be anonymous composite literal MyStruct.
Structs consist of data, but apart from this, structs also tell about the behavior in the form of methods. Methods attached to structs is very much similar to the definition of normal functions, the only variation is that you need to additionally specify its type.
Reflection is the ability of a program to introspect and analyze its structure during run-time. In Go language, reflection is primarily carried out with types. The reflect package offers all the required APIs/Methods for this purpose. Reflection is often termed as a method of metaprogramming.
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{})
}
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
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{})
}
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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With