在 Go 的模板系统领域,反射起着至关重要的作用。当动态调用具有不同接收器类型的接口上的方法时,就会出现挑战。{}虽然这可以与已知类型无缝配合,但当数据包装在接口{}中时,它会失败。
问题陈述
考虑以下内容code:
type Test struct { Start string } func (t *Test) Finish() string { return t.Start + "finish" } func Pass(i interface{}) { _, ok := reflect.TypeOf(&i).MethodByName("Finish") if ok { fmt.Println(reflect.ValueOf(&i).MethodByName("Finish").Call([]reflect.Value{})[0]) } else { fmt.Println("Pass() fail") } } func main() { i := Test{Start: "start"} Pass(i) _, ok := reflect.TypeOf(&i).MethodByName("Finish") if ok { fmt.Println(reflect.ValueOf(&i).MethodByName("Finish").Call([]reflect.Value{})[0]) } else { fmt.Println("main() fail") } }
观察
问题
问题在于当数据包装在接口{}中时访问数据的地址。使用通常指向指针的 &i 在这种情况下不起作用。
解决方案
为了解决这个问题,我们需要处理所有可能的情况:
interface{} 数据作为值,方法接收器作为value:
interface{} data as作为指针的值和方法接收器:
interface{} 数据作为指针,方法接收器作为值:
接口{}数据作为指针,方法接收者作为指针:
实现
基于这个逻辑,我们可以创建一个通用函数来动态调用方法:
func CallMethod(i interface{}, methodName string) interface{} { // Handle all scenarios var ptr, value, finalMethod reflect.Value value = reflect.ValueOf(i) 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 and pointer method := value.MethodByName(methodName) if method.IsValid() { finalMethod = method } method = ptr.MethodByName(methodName) if method.IsValid() { finalMethod = method } // Call the method if (finalMethod.IsValid()) { return finalMethod.Call([]reflect.Value{})[0].Interface() } // Method not found return "" }
通过此函数,我们现在可以动态调用方法,而不管接收器类型和接口如何{}包装:
i := Test{Start: "start"} j := Test{Start: "start2"} fmt.Println(CallMethod(i, "Finish")) // Output: startfinish fmt.Println(CallMethod(&i, "Finish")) // Output: startfinish fmt.Println(CallMethod(i, "Another")) // Output: fmt.Println(CallMethod(&i, "Another")) // Output: start2another fmt.Println(CallMethod(j, "Finish")) // Output: startfinish fmt.Println(CallMethod(&j, "Finish")) // Output: start2finish fmt.Println(CallMethod(j, "Another")) // Output: fmt.Println(CallMethod(&j, "Another")) // Output: start2another
以上是无论接收器类型如何,如何在 Go 中动态调用接口{}上的方法?的详细内容。更多信息请关注PHP中文网其他相关文章!