Introduction
- 反射,是指電腦程序在運行時(Run Time)可以訪問、檢測和修改它本身狀態或行為的一種能力。
- 程序在運行時能夠觀察並修改自己的行為
- 可以在運行時動態的獲取變量的相關信息,包括類型與值
- 在Golang中有提供官方的package可實現反射
- 主要有兩個函數
reflect.TypeOf
:獲取變量的類型,返回reflect.Type
類型reflect.ValueOf
:獲取變量的值,返回reflect.Value
類型reflect.Value.Kind
:可以獲取變量的類別,返回一個常量reflect.Value.Interface{}
:轉換成interface{}(空接口)類型
TypeOf
動態的獲取變量的類型1
func TypeOf (i interface{}) Type
- 可看到
reflect.TypeOf
接受空接口類型的參數,並返回reflect.Type
類型
1 | package main |
result
1 | b type: int |
- 透過
reflect.TypeOf
可直接獲取傳參進來的空接口為何種類型,但儲存類型的變量是reflect.Type
類型
ValueOf
- 透過
reflect.ValueOf
函數可將空接口變量轉換成reflect.Value
類型 reflect.Value
類型擁有很多方法,可獲取變量的相關信息(分析變量)
1 | func ValueOf (i interface{}) Value |
- 可看到
reflect.ValueOf
接受空接口類型的參數,並返回reflect.Value
類型
1 | package main |
result
1 | b value: 100 |
- 可看到其返回的類型為
reflect.Value
類型
.Kind() 查看變量類別
reflect.Value
類型的變量可調用.Kind()
方法查看變量的類別- 返回的是變量的類別,返回一個常量(
reflect.類別
) - 請注意類別與類型不一樣
1 | type Student struct{ |
- a變量為自訂義的
Student
類型 - a變量的類別為結構體(
struct
)
example
1 | package main |
result
1 | b type: main.Student |
.interface() 轉換成空接口類型
reflect.Value
類型的變量可調用.interface()
方法將類型轉換成interface{}
類型interface{}
空接口類型又可透過類型斷言,將變量從空接口轉成具體類型
example
1 | package main |
result
1 | b value: {Curtis 18} |
獲取變量的值
reflect.Value
類型可使用內置的方法,獲取變量的值
1 | reflect.ValueOf(x).Float() |
- 根據值不同的類型,獲取其值
要是空接口傳進來的值為int
類型,透過reflect.ValueOf()
函數轉成reflect.Value
類型
若使用reflect.Value
類型可調用的String()
方法獲取一開始具體類型為int
的值
則會獲取不到相對應的值
example
1 | package main |
result
1 | vl.Int(): 100 |
- 因從
b
參數傳進來的類型為int
類型,將其轉成string
類型時獲取不到值
設置變量的值
reflect.Value
類型可使用內置的方法,設置變量的值
- 其方法通常為
Set類型
作為方法名 - 只有
reflect.Value
的類型可以調用 - 注意!!!欲設置的值應為外部變量,因此傳參需傳入指針類型,否則panic
- 需搭配
Elem()
方法設置指針變量內部之值,相當於具體類型的*
1 | reflect.ValueOf(x).Elem().SetFloat() |
example
1 | package main |
[1] Elem()
函數為reflect.Value
類型可調用的方法
是向傳進來具體類型的指針指向的地址取值,相當於*
1 | var x *int = new(int) |
Elem()
就類似於變量前面加上*
result
1 | c = 10000 |
操作結構體
reflect.Value
類型可使用內置的方法,操作結構體- 其傳進來的空接口必須指向的是結構體類別,才能操作
.NumField() 查看結構體的字段數
1 | package main |
result
1 | 3 |
tips
reflect.Value
類型亦能調用.Field(n)
方法顯示字段信息,n
為第n
個字段- 若傳入為struct的指針類型,記得使用
Elem().Field(n)
方法顯示字段信息 - 字段的信息仍為
reflect.Value
類型,仍可對字段進行動態分析,或對字段進行操作 - 透過
Elem().Field(n).Set類型(值)
便可以對外部struct中的字段修改值
.NumMethod() 查看結構體的方法數
1 | package main |
result
1 | 2 |
.Method(n).Call 調用結構體中的方法
n
代表的是調用第n
個方法- 使用
Call
方法時須先初始化一個reflect.Value
類型的切片,或直接傳入nil
1 | package main |
result
1 | Reading Books |
Tag
在之前json的打包中,我們遍歷結構體時,透過json作為key找到其value作為結構體的字段名
其具體也是透過反射做到的
- 調用
Tag
方法必須為reflect.Type
類型 - 且
Tag
的類型為reflect.StructTag
類型,可調用Get()
方法尋找key
對應的value
為何
1 | package main |
result
1 | tag type = reflect.StructTag |
- json就是利用反射,得到tag的key為”json”,其對應的value 優先進行打包