反射 反射的基本介紹 17.3.1 基本介紹 1) 反射可以在運行時 動態獲取變數的各種信息, 比如變數的類型(type),類別(kind) 2) 如果是結構體變數,還可以獲取到結構體本身的信息(包括結構體的 欄位、 方法) 3) 通過反射,可以修改變數的值,可以調用關聯的方法。 4) 使用反射,需 ...
反射
反射的基本介紹
17.3.1 基本介紹
1) 反射可以在運行時 動態獲取變數的各種信息, 比如變數的類型(type),類別(kind)
2) 如果是結構體變數,還可以獲取到結構體本身的信息(包括結構體的 欄位、 方法)
3) 通過反射,可以修改變數的值,可以調用關聯的方法。
4) 使用反射,需要 import (“reflect”)
反射重要的函數和概念
1) reflect.TypeOf(變數名),獲取變數的類型,返回reflect.Type
2) reflect.Value(變數名),獲取變數的值,返回reflect.Value(是一個結構體類型)
3) 變數、interface{} 和 reflect.Value 是可以相互轉換的,這點在實際開發中,會經常使用到
反射的快速入門
快速入門說明
請編寫一個案例,演示對(基本數據類型、interface{}、reflect.Value)進行反射的基本操作
代碼演示,見下麵的表格:
請編寫一個案例,演示對(結構體類型、interface{}、reflect.Value)進行反射的基本操作
package main
import (
"reflect"
"fmt"
)
//專門演示反射
func reflectTest01(b interface{}) {
//通過反射獲取的傳入的變數的 type , kind, 值
//1. 先獲取到 reflect.Type
rTyp := reflect.TypeOf(b)
fmt.Println("rType=", rTyp)
//2. 獲取到 reflect.Value
rVal := reflect.ValueOf(b)
n2 := 2 + rVal.Int()
//n3 := rVal.Float()
fmt.Println("n2=", n2)
//fmt.Println("n3=", n3)
fmt.Printf("rVal=%v rVal type=%T\n", rVal, rVal)
fmt.Printf("rVal=%v rTyp type=%T\n", rVal, rTyp)
//下麵我們將 rVal 轉成 interface{}
iV := rVal.Interface()
//將 interface{} 通過斷言轉成需要的類型
num2 := iV.(int)
fmt.Println("num2=", num2)
}
//專門演示反射[對結構體的反射]
func reflectTest02(b interface{}) {
//通過反射獲取的傳入的變數的 type , kind, 值
//1. 先獲取到 reflect.Type
rTyp := reflect.TypeOf(b)
fmt.Println("rType=", rTyp)
//2. 獲取到 reflect.Value
rVal := reflect.ValueOf(b)
//3. 獲取 變數對應的Kind
//(1) rVal.Kind() ==>
kind1 := rVal.Kind()
//(2) rTyp.Kind() ==>
kind2 := rTyp.Kind()
fmt.Printf("kind =%v kind=%v\n", kind1, kind2)
//下麵我們將 rVal 轉成 interface{}
iV := rVal.Interface()
fmt.Printf("iv=%v iv type=%T \n", iV, iV)
//將 interface{} 通過斷言轉成需要的類型
//這裡,我們就簡單使用了一帶檢測的類型斷言.
//同學們可以使用 swtich 的斷言形式來做的更加的靈活
stu, ok := iV.(Student)
if ok {
fmt.Printf("stu.Name=%v\n", stu.Name)
}
}
type Student struct {
Name string
Age int
}
type Monster struct {
Name string
Age int
}
func main() {
//請編寫一個案例,
//演示對(基本數據類型、interface{}、reflect.Value)進行反射的基本操作
//1. 先定義一個int
var num int = 100
reflectTest01(num)
//2. 定義一個Student的實例
stu := Student{
Name : "tom",
Age : 20,
}
reflectTest02(stu)
}
反射的註意事項和細節
1) reflect.Value.Kind,獲取變數的類別,返回的是一個常量
2) Type 和 Kind 的區別:
Type 是類型, Kind 是類別, Type 和 Kind 可能是相同的,也 可能是不同的.
比如: var num int = 10 num 的 Type 是 int , Kind 也是 int
比如: var stu Student stu 的 Type 是 pkg1.Student , Kind 是 struct
3) 通過反射可以讓變數在interface{}和reflect.Value之間互相轉換。
4) 通過反射獲取的變數的值,要求數據類型配,比如x是int,那麼就應該用reflect.Value.int(),而不能用其他,否則會報panic的錯誤。
5) 通過反射的來修改變數, 註意當使用 SetXxx 方法來設置需要通過對應的指針類型來完成, 這樣才能改變傳入的變數的值, 同時需要使用到 reflect.Value.Elem()方法
package main
import (
"reflect"
"fmt"
)
//通過反射,修改,
// num int 的值
// 修改 student的值
func reflect01(b interface{}) {
//2. 獲取到 reflect.Value
rVal := reflect.ValueOf(b)
// 看看 rVal的Kind是
fmt.Printf("rVal kind=%v\n", rVal.Kind())
//3. rVal
//Elem返回v持有的介面保管的值的Value封裝,或者v持有的指針指向的值的Value封裝
rVal.Elem().SetInt(20)
}
func main() {
var num int = 10
reflect01(&num)
fmt.Println("num=", num) // 20
//你可以這樣理解rVal.Elem()
// num := 9
// ptr *int = &num
// num2 := *ptr //=== 類似 rVal.Elem()
}
package main
import (
"fmt"
"reflect"
)
func main() {
var str string = "tom" //ok
fs := reflect.ValueOf(&str) //ok fs -> string
fs.Elem().SetString("jack") //ok
fmt.Printf("%v\n", str) // jack
}
反射最佳實踐
1) 使用 反射來遍歷結構體的欄位, 調用結構體的方法,並 獲取結構體標簽的值
2) 使用反射的方式來獲取結構體的 tag 標簽, 遍歷欄位的值,修改欄位值,調用結構體方法(要求:通過傳遞地址的方式完成, 在前面案例上修改即可)
3) 定義了兩個函數 test1 和 test2,定義一個適配器函數用作統一處理介面(略,用反射實現即可)
4) 使用反射操作任意結構體類型:
5) 使用反射創建並操作結構體
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 fields\n", num) //4
//變數結構體的所有欄位
for i := 0; i < num; i++ {
fmt.Printf("Field %d: 值為=%v\n", i, val.Field(i))
//獲取到struct標簽, 註意需要通過reflect.Type來獲取tag標簽的值
tagVal := typ.Field(i).Tag.Get("json")
//如果該欄位於tag標簽就顯示,否則就不顯示
if tagVal != "" {
fmt.Printf("Field %d: tag為=%v\n", i, tagVal)
}
}
//獲取到該結構體有多少個方法
numOfMethod := val.NumMethod()
fmt.Printf("struct has %d methods\n", 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)
}
const介紹
package main
import (
"fmt"
)
func main() {
var num int
num = 9 //ok
//常量聲明的時候,必須賦值。
const tax int = 0
//常量是不能修改
//tax = 10
fmt.Println(num, tax)
//常量只能修飾bool、數值類型(int, float系列)、string 類型
//fmt.Println(b)
const (
a = iota
b
c
d
)
fmt.Println(a, b, c, d)//0 1 2 3
}