首頁 > 軟體

Go語言開發框架反射機制及常見函數範例詳解

2022-09-26 14:02:50

基本介紹

  • 反射可以在執行時動態獲取變數的各種資訊,比如變數的型別,類別
  • 如果是結構體變數,還可以獲取到結構體本身的資訊
  • 通過反射,可以修改變數的值,可以呼叫關聯的方法
  • 使用反射,需要import("reflect")

示意圖

反射中常見函數和概念

reflect.TypeOf(變數名)

獲取變數的型別,返回reflect.Type型別

reflect.ValueOf(變數名)

獲取變數的值,返回reflect.Value型別reflect.Value是一個結構體型別,通過reflect.Value,可以獲取到關於該變數的很多資訊

變數.interface{}和reflect.Value是可以相互轉換的

基本使用

package main
import (
   "fmt"
   "reflect"
)
/*
1.編寫案例,對基本資料型別,interface{},reflect.Value進行反射
2.編寫案例,對結構體,interface{},reflect.Value進行反射
*/
func reflectTest(b interface{}){
   //列印出傳參的type,kind,value
   fmt.Printf("b的型別為%v,b的kind為%v,value為%vn",reflect.TypeOf(b),reflect.ValueOf(b).Kind(),reflect.ValueOf(b))	//b的型別為int,b的kind為int,value為100
   //reflect.TypeOf(),reflect.ValueOf()返回的型別
   fmt.Printf("reflect.TypeOf()返回型別為%T,reflect.ValueOf()返回型別為%Tn",reflect.TypeOf(b),reflect.ValueOf(b)) //reflect.TypeOf()返回型別為*reflect.rtype,reflect.ValueOf()返回型別為reflect.Value
}
type Student struct {
   Name string
   age int
}
func reflectTest2(b interface{}){
   rTyp:=reflect.TypeOf(b)
   fmt.Println(rTyp) //main.Student
   rVal:=reflect.ValueOf(b)
   //將rVal轉換成interface{}
   iV:=rVal.Interface()
   fmt.Printf("iv=%v type=%Tn",iV,iV) //iv={張三 18} type=main.Student
   //因為Go語言是靜態語言,所以不能直接獲取結構體中指定的值,所以我需要將其斷言成需要的型別
   stu,ok:=iV.(Student)
   if ok{
   	fmt.Printf(stu.Name,stu.age)  //張三%!(EXTRA int=18)
   }
}
func main() {
   //1.編寫案例,對基本資料型別,interface{},reflect.Value進行反射
   var num int =100
   reflectTest(num)
   //2.編寫案例,對結構體,interface{},reflect.Value進行反射
   stu:=Student{
   	Name: "張三",
   	age:  18,
   }
   reflectTest2(stu)
}

反射注意事項

  • reflect.ValueKind,獲取的變數的類別,返回的是一個常數
  • Type是型別,Kind是類別,Type和Kind可能是相同的,也可能是不同的,例如結構體
  • 通過反射可以在讓變數在interface{}和reflect.Value之間相互轉換
  • 通過反射的方式獲取變數的值(並返回對應的型別),要求資料型別匹配,比如x是int,那麼就應該使用reflect.Value(x).Int(),而不能使用其他的,否則報panic
  • 通過反射來修改變數,注意當使用Setxxx方法來設定需要通過對應的指標型別來完成,這樣才能改變傳入變數的值,同時需要使用到reflect.Value.Elem()方法

反射的最佳實踐

使用反射來遍歷結構體的欄位,呼叫結構體的方法,並獲取結構體標籤的值

package main
import (
	"fmt"
	"reflect"
)
//定義了一個Monster結構體
type Monster struct {
	Name  string `json:"name"`
	Age   int `json:"monster_age"`
	Score float32 `json:"成績"`
	Sex   string
}
//方法,返回兩個數的和
func (s Monster) GetSum(n1, n2 int) int {
	return n1 + n2
}
//方法, 接收四個值,給s賦值
func (s Monster) Set(name string, age int, score float32, sex string) {
	s.Name = name
	s.Age = age
	s.Score = score
	s.Sex = sex
}
//方法,顯示s的值
func (s Monster) Print() {
	fmt.Println("---start~----")
	fmt.Println(s)
	fmt.Println("---end~----")
}
func TestStruct(a interface{}) {
	//獲取reflect.Type 型別
	typ := reflect.TypeOf(a)
	//獲取reflect.Value 型別
	val := reflect.ValueOf(a)
	//獲取到a對應的類別
	kd := val.Kind()
	//如果傳入的不是struct,就退出
	if kd !=  reflect.Struct {
		fmt.Println("expect struct")
		return
	}
	//獲取到該結構體有幾個欄位
	num := val.NumField()
	fmt.Printf("struct has %d fieldsn", num) //4
	//變數結構體的所有欄位
	for i := 0; i < num; i++ {
		fmt.Printf("Field %d: 值為=%vn", i, val.Field(i))
		//獲取到struct標籤, 注意需要通過reflect.Type來獲取tag標籤的值
		tagVal := typ.Field(i).Tag.Get("json")
		//如果該欄位於tag標籤就顯示,否則就不顯示
		if tagVal != "" {
			fmt.Printf("Field %d: tag為=%vn", i, tagVal)
		}
	}
	//獲取到該結構體有多少個方法
	numOfMethod := val.NumMethod()
	fmt.Printf("struct has %d methodsn", numOfMethod)
	//var params []reflect.Value
	//方法的排序預設是按照 函數名的排序(ASCII碼)
	val.Method(1).Call(nil) //獲取到第二個方法。呼叫它
	//呼叫結構體的第1個方法Method(0)
	var params []reflect.Value  //宣告了 []reflect.Value
	params = append(params, reflect.ValueOf(10))
	params = append(params, reflect.ValueOf(40))
	res := val.Method(0).Call(params) //傳入的引數是 []reflect.Value, 返回[]reflect.Value
	fmt.Println("res=", res[0].Int()) //返回結果, 返回的結果是 []reflect.Value*/
}
func main() {
	//建立了一個Monster範例
	var a Monster = Monster{
		Name:  "黃鼠狼精",
		Age:   400,
		Score: 30.8,
	}
	//將Monster範例傳遞給TestStruct函數
	TestStruct(a)	
}

以上就是Go語言開發框架反射機制及常見函數範例詳解的詳細內容,更多關於Go開發框架反射機制的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com