Go語言學(xué)習(xí)之反射的用法詳解
反射指的是運(yùn)行時動態(tài)的獲取變量的相關(guān)信息
1. reflect 包
類型是變量,類別是常量
reflect.TypeOf,獲取變量的類型,返回reflect.Type類型
reflect.ValueOf,獲取變量的值,返回reflect.Value類型
reflect.Value.Kind,獲取變量的類別,返回一個常量
reflect.Value.Interface(),轉(zhuǎn)換成interface{}類型

1.1 獲取變量類型
package main
import (
"fmt"
"reflect"
)
func Test(i interface{}) {
//反射數(shù)據(jù)類型
t := reflect.TypeOf(i)
fmt.Println("類型是", t)
//反射數(shù)據(jù)值
v := reflect.ValueOf(i)
fmt.Println("值是", v)
}
func main() {
a := "hello"
Test(a)
}輸出結(jié)果如下
類型是 string
值是 hello
package main
import (
"fmt"
"reflect"
)
type Student struct {
Name string
Age int
Score float32
}
func Test(i interface{}) {
//反射獲取類型
t := reflect.TypeOf(i)
fmt.Println("類型是", t)
//反射獲取值
v := reflect.ValueOf(i)
//判斷值的類別
c := v.Kind()
fmt.Println("類別是", c)
}
func main() {
var stu Student = Student{
Name: "張三",
Age: 18,
Score: 80,
}
Test(stu)
fmt.Println("-------------")
var num int = 10
Test(num)
}輸出結(jié)果如下
類型是 main.Student
類別是 struct
-------------
類型是 int
類別是 int
1.2 斷言處理類型轉(zhuǎn)換
package main
import (
"fmt"
"reflect"
)
type Student struct {
Name string
Age int
Score float32
}
func Test(i interface{}) {
t := reflect.TypeOf(i)
fmt.Println("類型是", t)
//類別
v := reflect.ValueOf(i)
c := v.Kind()
fmt.Println("類別是", c)
fmt.Printf("c的類型是%T\n", c)
fmt.Printf("v的類型是%T\n", v)
//轉(zhuǎn)換成接口
iv := v.Interface()
fmt.Printf("iv的類型%T\n", iv)
//斷言處理
stu_iv, err := iv.(Student)
if err {
fmt.Printf("stu_iv的類型%T\n", stu_iv)
}
}
func main() {
var stu Student = Student{
Name: "張三",
Age: 18,
Score: 80,
}
Test(stu)
}輸出結(jié)果如下
類型是 main.Student
類別是 struct
c的類型是reflect.Kind
v的類型是reflect.Value
iv的類型main.Student
stu_iv的類型main.Student
2. ValueOf
2.1 獲取變量值
reflect.valueof(x).Float()
reflect.valueof(x).Int()
reflect.valueof(x).String()
reflect.Valueof(x).Bool()
2.2 類型轉(zhuǎn)換
package main
import (
"fmt"
"reflect"
)
func Test(i interface{}) {
v := reflect.ValueOf(i)
fmt.Printf("v的類型是%T\n", v)
//轉(zhuǎn)換成指定類型
t := v.Int()
fmt.Printf("t的類型是%T\n", t)
}
func main() {
//類型不同的話會報錯
var num int = 100
Test(num)
}輸出結(jié)果如下
v的類型是reflect.Value
t的類型是int64
3. Value.Set
3.1 設(shè)置變量值
reflect.Value.SetFloat(),設(shè)置浮點數(shù)
reflect.value.SetInt(),設(shè)置整數(shù)
reflect.Value.SetString(),設(shè)置字符串
3.2 示例
package main
import (
"fmt"
"reflect"
)
func Test(i interface{}) {
v := reflect.ValueOf(i)
//更新值需要value的地址,否則會保存,Elem()表示指針*
v.Elem().SetInt(100)
result := v.Elem().Int()
fmt.Printf("result類型為 %T, 值為 %d\n", result, result)
}
func main() {
var num int = 10
Test(&num)
}輸出結(jié)果如下
result類型為 int64, 值為 100
4. 結(jié)構(gòu)體反射
反射出結(jié)構(gòu)體的屬性和方法數(shù)量
方法名需大寫,需要被跨包調(diào)用識別
4.1 查看結(jié)構(gòu)體字段數(shù)量和方法數(shù)量
package main
import (
"fmt"
"reflect"
)
//結(jié)構(gòu)體
type Student struct {
Name string
Age int
Score float32
}
//結(jié)構(gòu)體方法
func (s Student) Run() {
fmt.Println("Running")
}
func (s Student) Sleep() {
fmt.Println("Sleeping")
}
//使用反射查看結(jié)構(gòu)體字段數(shù)量和方法數(shù)量
func Test(i interface{}) {
v := reflect.ValueOf(i)
//類別判斷
if v.Kind() != reflect.Struct {
fmt.Println("不是結(jié)構(gòu)體")
return
}
//獲取結(jié)構(gòu)體中字段數(shù)量
stu_num := v.NumField()
fmt.Println("字段數(shù)量: ", stu_num)
//獲取結(jié)構(gòu)體中方法數(shù)量
stu_meth := v.NumMethod()
fmt.Println("方法數(shù)量: ", stu_meth)
}
func main() {
var stu Student = Student{
Name: "張三",
Age: 18,
Score: 88,
}
Test(stu)
}輸出結(jié)果如下
字段數(shù)量: 3
方法數(shù)量: 2
4.2 獲取結(jié)構(gòu)體屬性
package main
import (
"fmt"
"reflect"
)
type Student struct {
Name string
Age int
Score float32
}
func Test(i interface{}) {
v := reflect.ValueOf(i)
//獲取結(jié)構(gòu)體中每個屬性
for i := 0; i < v.NumField(); i++ {
//輸出屬性值
fmt.Printf("%d %v\n", i, v.Field(i))
//輸出屬性值的類型
fmt.Printf("%d %v\n", i, v.Field(i).Kind())
}
}
func main() {
var stu Student = Student{
Name: "張三",
Age: 18,
Score: 88,
}
Test(stu)
}輸出結(jié)果如下
0 張三
0 string
1 18
1 int
2 88
2 float32
4.3 更改屬性值
package main
import (
"fmt"
"reflect"
)
type Student struct {
Name string
Age int
Score float32
}
func Test(i interface{}, name string) {
v := reflect.ValueOf(i)
vk := v.Kind()
//判斷是都為指針并指向結(jié)構(gòu)體類型
if vk != reflect.Ptr && v.Elem().Kind() == reflect.Struct {
fmt.Println("expect struct")
return
}
//更改屬性值
v.Elem().Field(0).SetString(name)
//獲取結(jié)構(gòu)體中每個屬性
for i := 0; i < v.Elem().NumField(); i++ {
//輸出屬性值
fmt.Printf("%d %v\n", i, v.Elem().Field(i))
//輸出屬性值的類型
fmt.Printf("%d %v\n", i, v.Elem().Field(i).Kind())
}
}
func main() {
var stu Student = Student{
Name: "張三",
Age: 18,
Score: 88,
}
Test(&stu, "李四")
}輸出結(jié)果如下
0 李四
0 string
1 18
1 int
2 88
2 float32
4.4 Tag原信息處理
package main
import (
"encoding/json"
"fmt"
"reflect"
)
type Student struct {
Name string `json:"stu_name"`
Age int
Score float32
}
func Test(i interface{}, name string) {
v := reflect.ValueOf(i)
vk := v.Kind()
//判斷是都為指針并指向結(jié)構(gòu)體類型
if vk != reflect.Ptr && v.Elem().Kind() == reflect.Struct {
fmt.Println("expect struct")
return
}
//更改屬性值
v.Elem().Field(0).SetString(name)
//獲取結(jié)構(gòu)體中每個屬性
for i := 0; i < v.Elem().NumField(); i++ {
//輸出屬性值
fmt.Printf("%d %v\n", i, v.Elem().Field(i))
//輸出屬性值的類型
fmt.Printf("%d %v\n", i, v.Elem().Field(i).Kind())
}
}
func main() {
var stu Student = Student{
Name: "張三",
Age: 18,
Score: 88,
}
Test(&stu, "李四")
fmt.Println("----------------json原信息----------------")
result, _ := json.Marshal(stu)
fmt.Println("json原信息: ", string(result))
//反射獲取類型
st := reflect.TypeOf(stu)
s := st.Field(0)
fmt.Printf("Name原信息名稱: %s\n", s.Tag.Get("json"))
}
輸出結(jié)果如下
0 李四
0 string
1 18
1 int
2 88
2 float32
----------------json原信息----------------
json原信息: {"stu_name":"李四","Age":18,"Score":88}
Name原信息名稱: stu_name
5. 函數(shù)反射
Go 中函數(shù)是可以賦值給變量的
示例:
既然函數(shù)可以像普通的類型變量一樣,那么在反射機(jī)制中就和不同的變量是一樣的,在反射中函數(shù)和方法的類型(Type)都是reflect.Func,如果要調(diào)用函數(shù),通過 Value 的Call() 方法
package main
import (
"fmt"
"reflect"
)
func hello() {
fmt.Println("hello world")
}
func main() {
//反射使用函數(shù)
v := reflect.ValueOf(hello)
//類型判斷是否屬于reflect.func類型
if v.Kind() == reflect.Func {
fmt.Println("函數(shù)")
}
//反射調(diào)用函數(shù)
v.Call(nil) //Call中需要傳入的是切片
}輸出結(jié)果如下
函數(shù)
hello world
package main
import (
"fmt"
"reflect"
"strconv"
)
//反射調(diào)用傳參和返回值函數(shù)
func Test(i int) string {
return strconv.Itoa(i)
}
func main() {
v := reflect.ValueOf(Test)
//定義參數(shù)切片
params := make([]reflect.Value, 1)
//切片元素賦值
params[0] = reflect.ValueOf(20)
//反射調(diào)函數(shù)
result := v.Call(params)
fmt.Printf("result的類型是 %T\n", result)
//[]reflect.Value切片轉(zhuǎn)換string
s := result[0].Interface().(string)
fmt.Printf("s的類型是 %T ,值為 %s\n", s, s)
}
輸出結(jié)果如下
result的類型是 []reflect.Value
s的類型是 string ,值為 20
6. 方法反射
反射中方法的調(diào)用,函數(shù)和方法可以說其實本質(zhì)上是相同的,只不過方法與一個“對象”進(jìn)行了“綁定”,方法是“對象”的一種行為,這種行為是對于這個“對象”的一系列操作,例如修改“對象”的某個屬性
6.1 使用 MethodByName 名稱調(diào)用方法
package main
import (
"fmt"
"reflect"
"strconv"
)
//反射方法
type Student struct {
Name string
Age int
}
//結(jié)構(gòu)體方法
func (s *Student) SetName(name string) {
s.Name = name
}
func (s *Student) SetAge(age int) {
s.Age = age
}
func (s *Student) String() string {
return fmt.Sprintf("%p", s) + ",Name:" + s.Name + ",Age:" + strconv.Itoa(s.Age)
}
func main() {
//實例化
stu := &Student{"張三", 19}
//反射獲取值:指針方式
stuV := reflect.ValueOf(&stu).Elem()
fmt.Println("修改前: ", stuV.MethodByName("String").Call(nil)[0])
//修改值
params := make([]reflect.Value, 1) //定義切片
params[0] = reflect.ValueOf("李四")
stuV.MethodByName("SetName").Call(params)
params[0] = reflect.ValueOf(20)
stuV.MethodByName("SetAge").Call(params)
fmt.Println("修改后: ", stuV.MethodByName("String").Call(nil)[0])
}輸出結(jié)果如下
修改前: 0xc000004078,Name:張三,Age:19
修改后: 0xc000004078,Name:李四,Age:20
6.2 使用 method 索引調(diào)用方法
package main
import (
"fmt"
"reflect"
"strconv"
)
//反射方法
type Student struct {
Name string
Age int
}
//結(jié)構(gòu)體方法
func (s *Student) B(name string) {
s.Name = name
}
func (s *Student) A(age int) {
s.Age = age
}
func (s *Student) C() string {
return fmt.Sprintf("%p", s) + ",Name:" + s.Name + ",Age:" + strconv.Itoa(s.Age)
}
func main() {
//實例化
stu := &Student{"張三", 19}
//反射獲取值:指針方式
stuV := reflect.ValueOf(&stu).Elem()
//索引調(diào)用方法
fmt.Println("修改前: ", stuV.Method(2).Call(nil)[0])
params := make([]reflect.Value, 1)
params[0] = reflect.ValueOf("李四")
stuV.Method(1).Call(params)
params[0] = reflect.ValueOf(20)
stuV.Method(0).Call(params)
fmt.Println("修改后: ", stuV.Method(2).Call(nil)[0])
//調(diào)用索引大小取決于方法名稱的ASCII大小進(jìn)行排序
}輸出結(jié)果如下
修改前: 0xc000004078,Name:張三,Age:19
修改后: 0xc000004078,Name:李四,Age:20
到此這篇關(guān)于Go語言學(xué)習(xí)之反射的用法詳解的文章就介紹到這了,更多相關(guān)Go語言反射內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang10進(jìn)制轉(zhuǎn)16進(jìn)制的幾種方法代碼示例
這篇文章主要給大家介紹了關(guān)于Golang10進(jìn)制轉(zhuǎn)16進(jìn)制的幾種方法,進(jìn)制轉(zhuǎn)換是Golang的一些基本操作,文中通過實例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-07-07

