瞭解go的反射
- 反射:與 物件導向中的反射類似!
- 程式執行中,獲取到的資訊(變數:型別,值。 結構體:欄位,方法)
- 可以透過反射機制,來改變變數和值
- reflect 包, 實現反射
- reflect.Type : 型別。 指示某一種型別
- reflect.Value : 值
- reflect.Kind : 種類。用於反射場景更多,指代的是相似的一些型別,當遇到自定義型別時,使用kind獲取原始種類
- 為什麼使用反射
- 編寫函式時,不知道函式傳遞的引數是什麼型別。使用any接收一切引數,利用反射 reflect 識別資料型別,種類
- 使用者輸入資料型別不清楚時,利用反射獲取資料型別,種類。 程式執行期間動態處理
靜態型別和動態型別
- 在反射過程中。程式編譯的時候變數型別為靜態型別,程式執行過程中變數是動態型別。
- 靜態型別:
- 動態型別:
- var A interface {} // 靜態型別
- A = 10 // interface 靜態型別, 執行時屬於動態型別 int
- A = "abcd" // interface 靜態型別, 執行時屬於動態型別 string
反射使用
package main
import (
"fmt"
"reflect"
)
type AType int
type User struct {
name string
age int
sex bool
custom AType
}
// 必須是暴露出來的方法,小寫的私有方法不被檢視到
func (u User) Say(content string) {
fmt.Printf("使用者:%s 。 說:%s\n", u.name, content)
}
func (u User) GetInfo() {
fmt.Printf("使用者名稱:%s,使用者年齡:%d,使用者性別:%d", u.name, u.age, u.sex)
}
func analyticDataFunc(v interface{}) {
// v 是一個任意變數
// 1. 獲取 v 的型別。 一般使用Kind,Type會被使用者自定
getType := reflect.TypeOf(v) // Type 型別
fmt.Println("v type:", getType)
// 1.1 . 透過vType 獲取 v 的 名稱
vName := getType.Name()
fmt.Println("v name:", vName)
// 1.2 . 透過 vType 獲取 Kind 的種類
vKind := getType.Kind()
fmt.Println("v kind:", vKind)
// 2. 獲取 v 變數中的值
getValue := reflect.ValueOf(v)
fmt.Println("v value:", getValue)
// 2.1 遍歷欄位 和 具體的值:
// getValue.NumField() 獲取欄位和值,該函式返回長度。非可遍歷
for index := 0; index < getValue.NumField(); index++ {
vFieldName := getType.Field(index) // 欄位資訊
vFieldValue1 := getValue.Field(index) // 值
fmt.Println("v變數包含的資訊:", "欄位:", vFieldName, "值:", vFieldValue1)
}
// 2.2 遍歷方法
// getType.NumMethod() 獲取 大寫開頭的 暴露的出來的函式數量!
for i := 0; i < getType.NumMethod(); i++ {
method := getType.Method(i)
fmt.Printf("method Name:【%s】 method Type:【%v】\n", method.Name, method.Type)
}
}
func main() {
/*
反射場景
- 透過反射獲取資訊
*/
var u = User{"張三", 3, true, 1000000}
fmt.Println("define u data:", u)
u.Say("你好")
u.GetInfo()
analyticDataFunc(u)
}
透過反射修改資料
package main
import (
"fmt"
"reflect"
)
func main() {
/* 透過反射修改資訊 */
// 透過反射修改值,需要操作物件的指標,拿到地址
var num = 3.14
pointer := reflect.ValueOf(&num)
newValue := pointer.Elem() // 獲取指標物件
fmt.Println("型別:", newValue.Type()) // 使用 Kind()
fmt.Println("判斷該型別能否修改:", newValue.CanSet())
newValue.SetFloat(2.11) // 透過反射修改資料
fmt.Println("newValue:", newValue)
}
透過反射修改變數的函式
package main
import (
"fmt"
"reflect"
)
func reflectionModifiedVariableFunc(v any) reflect.Value {
// 透過反射修改變數
pointer := reflect.ValueOf(v) // 操作地址物件
newValue := pointer.Elem()
newValueType := newValue.Kind()
fmt.Println(" [待修改變數] 值:", newValue)
fmt.Println(" [待修改變數] 原型別:", newValueType)
fmt.Println(" [待修改變數] 是否能修改:", newValue.CanSet())
if newValueType == reflect.Int {
newValue.SetInt(100)
}
if newValueType == reflect.String {
newValue.SetString("李四")
}
return newValue
}
func main() {
var modifyVal1 = 1
var modifyVal2 = "你好"
fmt.Println("【修改前】", modifyVal1, modifyVal2)
res1 := reflectionModifiedVariableFunc(&modifyVal1)
res2 := reflectionModifiedVariableFunc(&modifyVal2)
fmt.Println("【修改後】", res1, res2)
}
透過反射修改結構體
package main
import (
"fmt"
"reflect"
)
type Student struct {
Name string
age int
Age int
}
func main() {
/* 透過反射修改結構體變數資料 */
var stu = Student{"張三", 19, 20}
fmt.Println("stu:", stu)
stuStructValue := reflect.ValueOf(&stu)
if stuStructValue.Kind() == reflect.Ptr {
// 獲取指標物件
newValue := stuStructValue.Elem()
if newValue.CanSet() {
// 找到要修改結構體的欄位
// 反射:欄位必須大寫開頭 , 小寫觸發恐慌 panic
newValue.FieldByName("Name").SetString("lisi")
newValue.FieldByName("Age").SetInt(333)
}
}
fmt.Println("反射修改後的stu:", stu)
}
反射呼叫結構體方法
- MethodByName 找到函式名。必須大寫
- Call 執行 函式。
package main
import (
"fmt"
"reflect"
)
type StudentA struct {
Name string
Age int
}
func (s StudentA) Say(content string) {
/* 傳入結構體方法 有引數*/
fmt.Println(s.Name, "Say:【", content, "】")
}
func (s StudentA) GetInfo() {
/* 傳入結構體方法 無引數*/
fmt.Printf("name: %s, age: %d\n", s.Name, s.Age)
}
func main() {
/*
透過 反射修 呼叫方法
- 1. MethodByName 找到函式名。必須大寫
- 2. Call 執行 函式。
- 2.1 無引數 傳入 nil
- 2.2 有引數,傳入Value型別
*/
var stu = StudentA{"張三", 20}
fmt.Println("stu:", stu)
stuStructValue := reflect.ValueOf(&stu)
fmt.Printf("stuStructValue Kind:%s, Type:%s\n", stuStructValue.Kind(), stuStructValue.Type())
if stuStructValue.Kind() == reflect.Ptr {
// 獲取指標物件
newValue := stuStructValue.Elem()
fmt.Printf("newValue Kind:%s, Type:%s\n", newValue.Kind(), newValue.Type())
// 無參函式,透過反射呼叫 . 傳入 nil
newValue.MethodByName("GetInfo").Call(nil)
// 有參函式,透過反射呼叫。 傳入 []Value
args := make([]reflect.Value, 1) // make 定義引數型別
args[0] = reflect.ValueOf("這是反射呼叫") // 必須傳入 Value型別的引數 。張三 Say:【 這是反射呼叫 】
newValue.MethodByName("Say").Call(args)
}
}
反射呼叫普通函式
package main
import (
"fmt"
"reflect"
)
func f1() {
fmt.Println("無參函式")
}
func f2(i string) {
fmt.Println("有參函式:", i)
}
func f3(i, s string) string {
fmt.Println("有參函式,有返回值:", i, s)
return s
}
func main() {
/*
反射呼叫函式
*/
// 無參
v1 := reflect.ValueOf(f1)
fmt.Println(v1.Kind(), v1.Type())
v1.Call(nil)
// 有參
v2 := reflect.ValueOf(f2)
fmt.Println(v2.Kind(), v2.Type())
v2Args := make([]reflect.Value, 1)
v2Args[0] = reflect.ValueOf("你好")
v2.Call(v2Args)
// 有參有返回值
v3 := reflect.ValueOf(f3)
fmt.Println(v3.Kind(), v3.Type())
v3Args := make([]reflect.Value, 2)
v3Args[0] = reflect.ValueOf("不好")
v3Args[1] = reflect.ValueOf("aaa")
v3.Call(v3Args)
}