模板系统严重依赖Go中的reflect包。使用此包时,在动态调用接口上的方法时可能会遇到困难。当数据类型存储为接口{}时,这个问题变得很明显。
理解接口方法调用的动态至关重要。有四种场景需要考虑:
反射可以帮助确定接口的底层数据值。有了这些信息,就可以生成替代数据类型并区分值和指针接收器方法。
要解决此问题,需要创建值和指针表示形式数据的:
value := reflect.ValueOf(data) if value.Type().Kind() == reflect.Ptr { ptr = value value = ptr.Elem() // acquire value referenced by pointer } else { ptr = reflect.New(reflect.TypeOf(i)) // create new pointer temp := ptr.Elem() // create variable to value of pointer temp.Set(value) // set value of variable to our passed in value }
两种数据类型都可用,检查方法是否存在就变成了简单明了:
var finalMethod reflect.Value method := value.MethodByName(methodName) if method.IsValid() { finalMethod = method } // check for method on pointer method = ptr.MethodByName(methodName) if method.IsValid() { finalMethod = method } if (finalMethod.IsValid()) { return finalMethod.Call([]reflect.Value{})[0].String() }
通过采用这种方法,可以有效地动态调用任何方法,无论它是定义为值还是指针接收器。
package main import ( "fmt" "reflect" ) type Test struct { Start string } // value receiver func (t Test) Finish() string { return t.Start + "finish" } // pointer receiver func (t *Test) Another() string { return t.Start + "another" } func CallMethod(i interface{}, methodName string) interface{} { var ptr reflect.Value var value reflect.Value var finalMethod reflect.Value value = reflect.ValueOf(i) // if we start with a pointer, we need to get value pointed to // if we start with a value, we need to get a pointer to that value if value.Type().Kind() == reflect.Ptr { ptr = value value = ptr.Elem() } else { ptr = reflect.New(reflect.TypeOf(i)) temp := ptr.Elem() temp.Set(value) } // check for method on value method := value.MethodByName(methodName) if method.IsValid() { finalMethod = method } // check for method on pointer method = ptr.MethodByName(methodName) if method.IsValid() { finalMethod = method } if (finalMethod.IsValid()) { return finalMethod.Call([]reflect.Value{})[0].Interface() } // return or panic, method not found of either type return "" } func main() { i := Test{Start: "start"} j := Test{Start: "start2"} fmt.Println(CallMethod(i, "Finish")) fmt.Println(CallMethod(&i, "Finish")) fmt.Println(CallMethod(i, "Another")) fmt.Println(CallMethod(&i, "Another")) fmt.Println(CallMethod(j, "Finish")) fmt.Println(CallMethod(&j, "Finish")) fmt.Println(CallMethod(j, "Another")) fmt.Println(CallMethod(&j, "Another")) }
输出:
startfinish startfinish <nil> startanother startfinish startfinish <nil> startanother
以上是如何在 Go 中动态调用接口方法,同时处理值接收器和指针接收器?的详细内容。更多信息请关注PHP中文网其他相关文章!