调用以指针作为接收者的指针的方法表达式
There is func someFunc(v interface{}, fn interface{})
where v
is a pointer to struct (say *A
) and fn is a method expression (say (*A).method()
). How to call fn
with v
as parameter (using reflect
)?
有 func someFunc(v interface {},fn interface {}) code>其中 v code>是指向struct的指针(例如
* A code>),而fn是方法表达式(例如
(* A).method() code>)。 如何使用
v code>作为参数调用
fn code>(使用
reflect code>)? p>
div>
It's possible to do with reflect.Value.Call()
, however it's pretty cumbersome, especially if you need to do something with the return value. But here's a basic example:
package main
import (
"fmt"
"reflect"
)
type Foo struct {
Bar string
}
func concrete(foo *Foo) {
fmt.Printf("Foo: %#v
", foo)
}
func someFunc(v interface{}, fn interface{}) {
f := reflect.ValueOf(fn)
arg := reflect.ValueOf(v)
f.Call([]reflect.Value{arg})
}
func main() {
foo := Foo{"Bar"}
someFunc(&foo, concrete)
}
// Output: Foo: &main.Foo{Bar:"Bar"}
https://play.golang.org/p/ED6QdvENxti
If you want to call a method of a struct by name, we need to revise it just a bit:
type Foo struct {
Bar string
}
func (f *Foo) Concrete() {
fmt.Printf("Foo: %#v
", f)
}
func callByName(v interface{}, fn string) {
arg := reflect.ValueOf(v)
f := arg.MethodByName(fn)
f.Call([]reflect.Value{})
}
func main() {
foo := Foo{"Bar"}
callByName(&foo, "Concrete")
}
Notice that in this case the method value is already bound to the struct instance, so we don't need to pass it anything as an argument.
So to the challenge of using reflect to call a method without knowing name and being a method of the same type, as op (orignally) addressed, I have come up with a very, very ugly and unsafe way.
package main
import (
"fmt"
"reflect"
"runtime"
"strings"
)
func getFName(v reflect.Value) string {
return runtime.FuncForPC(v.Pointer()).Name()
}
func callMethod(a,fn interface{}) {
fname:=getFName(reflect.ValueOf(fn))
parts:=strings.Split(fname,".")
name:=strings.TrimSuffix(parts[len(parts)-1],"-fm")
reflect.ValueOf(a).MethodByName(name).Call(nil)
}
func (s *a) Tst() {
fmt.Println(s)
}
type a struct { p string }
func main() {
x,y:=a{"Good"},a{"Bad"}
callMethod(&x,y.Tst)
}
Playground: https://play.golang.org/p/QgrGhI5DC3p
I would like to know a better way that does not rely on how runtime.FuncForPc
formats methods (which is not version-safe), but this is the best I have. Please do tell me any ideas to improve it.