速戰速決 go - go 高級: 反射(通過反射獲取變量的類型,通過反射獲取變量的值,修改變量的值,獲取結構體成員的值,修改結構體成員的值,調用結構體的方法,通過反射實例化,通過反射調用函數)

速戰速決 go https://github.com/webabcd/GoSample
作者 webabcd

速戰速決 go - go 高級: 反射(通過反射獲取變量的類型,通過反射獲取變量的值,修改變量的值,獲取結構體成員的值,修改結構體成員的值,調用結構體的方法,通過反射實例化,通過反射調用函數)

示例如下:

advanced/reflect.go

// go 高級 - 反射(通過反射獲取變量的類型,通過反射獲取變量的值,修改變量的值,獲取結構體成員的值,修改結構體成員的值,調用結構體的方法,通過反射實例化,通過反射調用函數)

package advanced

import (
	"fmt"
	"reflect"
)

func ReflectSample() {
	// 通過反射獲取變量的類型
	reflect_Sample1()
	// 通過反射獲取變量的值,修改變量的值,獲取結構體成員的值,修改結構體成員的值,調用結構體的方法
	reflect_Sample2()
	// 通過反射實例化,通過反射調用函數
	reflect_Sample3()
}

func reflect_Sample1() {
	a := myStruc2{salary: 3500, myStruct1: myStruct1{name: "webabcd", age: 40}}

	// reflect.TypeOf() - 通過反射獲取指定變量的類型
	//   Name() - 類型的名稱
	//   Kind() - 類型的種類
	b := reflect.TypeOf(a)
	fmt.Println(b, b.Name(), b.Kind()) // advanced.myStruc2 myStruc2 struct

	c := &a
	d := reflect.TypeOf(c)
	// 如果是指針類型,則可以通過 Elem() 獲取指針指向的值的類型
	fmt.Println(d.Kind(), d.Elem(), d.Elem().Name(), d.Elem().Kind()) // ptr advanced.myStruc2 myStruc2 struct

	// 遍歷結構體的所有成員
	for i := 0; i < b.NumField(); i++ {
		// 獲取結構體的指定索引的成員
		field := b.Field(i)
		// 獲取成員的類型,名稱,標籤
		fmt.Printf("type:%v, name:%v, tag:%v\n", field.Type, field.Name, field.Tag)
	}
	// type:float32, name:salary, tag:
	// type:advanced.myStruct1, name:myStruct1, tag:
	// type:float32, name:SalaryExpected, tag:
	// type:*int, name:ptr, tag

	// 根據名稱查找結構體的指定成員
	field_salary, ok_salary := b.FieldByName("salary")
	fmt.Println(field_salary.Type, ok_salary) // float32 true
	// 根據索引查找結構體的指定成員(這種方式支持多級查找,本例先找 myStruc2 的索引爲 1 的成員,即 myStruct1,然後再找 myStruct1 的索引爲 0 的成員,即 name)
	field_name := b.FieldByIndex([]int{1, 0})
	fmt.Println(field_name.Type) // string
}

func reflect_Sample2() {
	var a int = 123

	// reflect.ValueOf() - 通過反射獲取指定變量的值
	//   Int(), Float(), String() 等 - 按指定的類型獲取值
	//   Interface() - 獲取接口,後續可以通過類型斷言的方式獲取值
	b := reflect.ValueOf(a)
	// 通過反射獲取變量的值
	c := b.Int()
	// 通過反射獲取變量的值(用斷言的方式)
	d, ok := b.Interface().(int)
	fmt.Println(c, d, ok) // 123 123 true

	// 通過反射修改變量的值(下面這種方式會報錯,因爲傳入的是 a 的值,它是不能被尋址的,即不知道 a 的值保存在哪裏)
	// reflect.ValueOf(a).SetInt(456)
	// 通過反射修改變量的值(下面這種方式是正確的,因爲傳入的是 a 的指針,它是可以被尋址的,即知道 a 的值保存在哪裏)
	reflect.ValueOf(&a).Elem().SetInt(456)
	fmt.Println(a) // 456

	e := myStruc2{salary: 3500, myStruct1: myStruct1{name: "webabcd", age: 40}, SalaryExpected: 4000}
	f := reflect.ValueOf(e)
	// 通過反射獲取結構體的指定成員的值(獲取結構體成員的其他方式可以參見上面的 reflect_Sample1() 中的代碼,方法是類似的)
	field_salaryExpected := f.FieldByName("SalaryExpected")
	fmt.Println(field_salaryExpected.Float()) // 4000

	// 如果找不到指定的成員則 IsValid() 爲 false
	// 如果值是 nil 則 IsNil() 爲 true
	fmt.Println(f.FieldByName("xxx").IsValid(), f.FieldByName("ptr").IsNil()) // false true

	// 通過反射修改結構體的指定成員的值
	// 下面這種方式會報錯,雖然傳入的是 e 的指針,是可被尋址的,但是修改的成員是不可導出的,即小寫開頭的
	// reflect.ValueOf(&e).Elem().FieldByName("salary").SetFloat(5000)
	// 下面這種方式是正確的,也就是說傳入的是 e 的指針,是可被尋址的,並且修改的成員是可導出的,即大寫開頭的
	reflect.ValueOf(&e).Elem().FieldByName("SalaryExpected").SetFloat(5000)
	fmt.Println(e.SalaryExpected) // 5000

	// 通過反射調用結構體的方法
	// 拿到方法,必須是可導出的,即大寫開頭的
	method := reflect.ValueOf(&e).MethodByName("Show")
	// 構造參數
	args := []reflect.Value{reflect.ValueOf("v1"), reflect.ValueOf("v2")}
	// 調用方法並傳參,然後獲取結果
	g := method.Call(args)[0]
	fmt.Println(g) // name:webabcd, age:40, p1:v1, p2:v2
}

func reflect_Sample3() {
	a := 123
	// 通過反射獲取指定變量的類型
	b := reflect.TypeOf(a)
	// 根據反射類型創建類型實例
	c := reflect.New(b)
	// 通過反射修改變量的值
	c.Elem().SetInt(456)
	// 通過反射獲取變量的值
	fmt.Println(a, c.Elem().Int()) // 123 456

	// 通過反射調用函數
	// 拿到函數
	method := reflect.ValueOf(myFunc1)
	// 構造參數
	args := []reflect.Value{reflect.ValueOf("v1"), reflect.ValueOf("v2")}
	// 調用函數並傳參,然後獲取結果
	d := method.Call(args)[0]
	fmt.Println(d) // p1:v1, p2:v2
}

type myStruct1 struct {
	name string
	age  int
}

type myStruc2 struct {
	salary float32
	myStruct1
	SalaryExpected float32
	ptr            *int
}

func (s *myStruc2) Show(p1, p2 string) string {
	return fmt.Sprintf("name:%s, age:%d, p1:%s, p2:%s", s.name, s.age, p1, p2)
}

func myFunc1(p1, p2 string) string {
	return fmt.Sprintf("p1:%s, p2:%s", p1, p2)
}

速戰速決 go https://github.com/webabcd/GoSample
作者 webabcd

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章