反射
反射的基本介紹
17.3.1 基本介紹
- 反射可以在運行時 動態獲取變數的各種資訊, 比如變數的型別(type),類別(kind)
- 如果是結構體變數,還可以獲取到結構體本身的資訊(包括結構體的 欄位、 方法)
- 通過反射,可以修改變數的值,可以呼叫關聯的方法,
- 使用反射,需要 import (“reflect”)
反射重要的函式和概念
-
reflect.TypeOf(變數名),獲取變數的型別,回傳reflect.Type
-
reflect.Value(變數名),獲取變數的值,回傳reflect.Value(是一個結構體型別)
-
變數、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)
}
反射的注意事項和細節
-
reflect.Value.Kind,獲取變數的類別,回傳的是一個常量
-
Type 和 Kind 的區別:
Type 是型別, Kind 是類別, Type 和 Kind 可能是相同的,也 可能是不同的.
比如: var num int = 10 num 的 Type 是 int , Kind 也是 int
比如: var stu Student stu 的 Type 是 pkg1.Student , Kind 是 struct -
通過反射可以讓變數在interface{}和reflect.Value之間互相轉換,
-
通過反射獲取的變數的值,要求資料型別配,比如x是int,那么就應該用reflect.Value.int(),而不能用其他,否則會報panic的錯誤,
-
通過反射的來修改變數, 注意當使用 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
}
反射最佳實踐
-
使用 反射來遍歷結構體的欄位, 呼叫結構體的方法,并 獲取結構體標簽的值
-
使用反射的方式來獲取結構體的 tag 標簽, 遍歷欄位的值,修改欄位值,呼叫結構體方法(要求:通過傳遞地址的方式完成, 在前面案例上修改即可)
-
定義了兩個函式 test1 和 test2,定義一個配接器函式用作統一處理介面(略,用反射實作即可)
-
使用反射操作任意結構體型別:
-
使用反射創建并操作結構體
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
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/59775.html
標籤:Go
