调用以指针作为接收者的指针的方法表达式

问题描述:

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.