模板系統非常依賴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中文網其他相關文章!