1. 程式人生 > >Golang反射的使用

Golang反射的使用

反射的基本介紹:

1)反射可以在執行時動態獲取變數的各種資訊,比如變數的型別(type),類別(kind)

2)如果是結構體變數,還可以獲取到結構體本身的資訊(包含結構體的欄位、方法

3)通過反射,可以修改變數的值,可以呼叫關聯的方法。

 

 

型別的相互轉換:

例項1:

package main

import (
	"fmt"
	"reflect"
)

type student struct {
	Name string
	Age  int
}

//修改簡單資料型別
func changeData(i interface{}) {
	value := reflect.ValueOf(i)
	fmt.Println("kind:", value.Kind())
	value.Elem().SetInt(30)

}

//修改結構體型別資料
func ModityData(i interface{}) {
	value := reflect.ValueOf(i)
	fmt.Println(value.Kind())
	value.Elem().Field(0).SetString("江洲你好!")
	value.Elem().Field(1).SetInt(33333)

}

//通過反射 改變結構體對應欄位的值
func main() {
	stu := student{Name: "jiangzhou", Age: 23}
	fmt.Println("原資料為:", stu)
	ModityData(&stu)
	fmt.Println("通過反射改變資料之後的值為:", stu)
}

//通過反射改變基本資料型別的值
func main02() {
	num := 10
	fmt.Println("原來資料是:", num)
	changeData(&num)
	fmt.Println("改變之後的資料是:", num)

}

//反射的基本使用: 型別  value  以及value與資料的相互轉換
func main01() {
	stu := student{Name: "jiangzhou", Age: 23}
	typeOf := reflect.TypeOf(stu)
	fmt.Println(typeOf, typeOf.Kind())
	fmt.Println("執行value操作")
	value := reflect.ValueOf(stu)
	fmt.Println(value)
	interface1 := value.Interface()
	stu1 := interface1.(student)
	fmt.Println(stu1, stu1.Name, stu1.Age)

}

例項2:

package main

import (
	"fmt"
	"reflect"
)

/*
主要功能:1、取出結構體 tag中json對應的內容
*/
type Stu struct {
	Name string `json:"name"`
	Age int `json:"age"`
	Address string `json:"address"`
}

func(s Stu) Print1()  {
	fmt.Println(s.Name,s.Age,s.Address)
}
func (s Stu)Update1 () {
	s.Address="重慶"
	s.Age+=1
}
func (s Stu)GetNum(n1 ,n2 int) int {
	return n1+n2
}


func showTag(i interface{})  {
	if reflect.TypeOf(i).Kind()!=reflect.Struct {
		fmt.Println("不是結構體型別 ")
		return
	}
	varlue:=reflect.ValueOf(i)
	type1:=reflect.TypeOf(i)
	num:=varlue.NumField()
	for i:=0;i<num;i++{
		fmt.Print("結構體第",i,"個欄位為:",varlue.Field(i))
		tag:=type1.Field(i).Tag  //獲取標籤
		fmt.Println("\t",tag,tag.Get("json")/*獲取json對應的便籤*/)
	}
	fmt.Println("執行方法的呼叫")
	num1 := varlue.NumMethod()  //標記:方法名一定要大寫,不然會找不到對應的方法
	fmt.Println("結構體方法的個數為:",num1)
	varlue.Method(1).Call(nil)
	args:=[]reflect.Value{reflect.ValueOf(10),reflect.ValueOf(20)}

	call := varlue.Method(0).Call(args)
	fmt.Println(call[0].Int())
	fmt.Println("通過斷言來執行的操作:")
	i2 := call[0].Interface()
	res,ok:=i2.(int)
	fmt.Println(res,ok)

}
func main() {
	stu:=Stu{Name:"jiangzhou",Age:23,Address:"cq"}
	showTag(stu)
}

例項3

以下例項為測試用例(golang自帶了測試框架,測試檔案命名規則:XX_test.go。如以下例項的檔名稱為:refelect_test.go;測試方法名稱命名規則為:TestXxx,以下測試方法名稱為:TestReflectFunc)

package main

import (
	"testing"
)

func TestReflectFunc(t *testing.T)  {
	call1:= func(v1 int,v2 int) {
		t.Log(v1,v2)
	}
	call2:= func(v1 int,v2 int,s string) {
		t.Log(v1,v2,s)
	}
	call1(1,2)
	call2(1,2,"call2")

}

通過反射完成測試方法的呼叫:

package main

import (
	"reflect"
	"testing"
)

func TestReflectFunc(t *testing.T)  {
	call1:= func(v1 int,v2 int) {
		t.Log(v1,v2)
	}
	call2:= func(v1 int,v2 int,s string) {
		t.Log(v1,v2,s)
	}
	var (
		function reflect.Value
		inValue []reflect.Value
		n int
	)
	bridge:= func(call interface{},args ...interface{}) {
		n=len(args)
		inValue=make([]reflect.Value,n)
		for i:=0;i<n;i++{
			inValue[i]=reflect.ValueOf(args[i])
		}
		function=reflect.ValueOf(call)
		function.Call(inValue)
	}
	bridge(call1,1,2)
	bridge(call2,1,2,"call2")

}

不知道介面 呼叫那個 函式,根據傳入引數在執行時確定呼叫的具體介面,這種需要對函式或方法反射。如以上這種橋接模式:

bridge:= func(call interface{},args ...interface{}){...}

第一個引數call以介面的形式傳入函式指標,函式引數args以可變引數的形式傳入,bridge函式中可以用反射來動態執行call函式。

例項4

package main

import (
	"encoding/json"
	"fmt"
)

type Stu1 struct {
	Name string `json:"stuName"`
	Age int `json:"stuAge`
	Address string `json:"StuAddress"`
}
func main() {
	stu:=Stu1{Name:"jiangzhou",Age:23,Address:"cq"}
	data,_:=json.Marshal(stu)
	fmt.Println(string(data))
}

對結構體序列化時,如果結構體有指定Tag,goland底層也會使用到反射生成對應的字串。