Go語言學(xué)習(xí)之反射的用法詳解
反射指的是運(yùn)行時(shí)動(dòng)態(tài)的獲取變量的相關(guān)信息
1. reflect 包
類型是變量,類別是常量
reflect.TypeOf,獲取變量的類型,返回reflect.Type類型
reflect.ValueOf,獲取變量的值,返回reflect.Value類型
reflect.Value.Kind,獲取變量的類別,返回一個(gè)常量
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() { //類型不同的話會(huì)報(bào)錯(cuò) var num int = 100 Test(num) }
輸出結(jié)果如下
v的類型是reflect.Value
t的類型是int64
3. Value.Set
3.1 設(shè)置變量值
reflect.Value.SetFloat(),設(shè)置浮點(diǎn)數(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的地址,否則會(huì)保存,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)用識(shí)別
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)體中每個(gè)屬性 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)體中每個(gè)屬性 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)體中每個(gè)屬性 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ù)和方法可以說其實(shí)本質(zhì)上是相同的,只不過方法與一個(gè)“對(duì)象”進(jìn)行了“綁定”,方法是“對(duì)象”的一種行為,這種行為是對(duì)于這個(gè)“對(duì)象”的一系列操作,例如修改“對(duì)象”的某個(gè)屬性
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() { //實(shí)例化 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() { //實(shí)例化 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)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語言實(shí)現(xiàn)的web爬蟲實(shí)例
這篇文章主要介紹了Go語言實(shí)現(xiàn)的web爬蟲,實(shí)例分析了web爬蟲的原理與Go語言的實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-02-02Go語言定時(shí)任務(wù)的實(shí)現(xiàn)示例
本文主要介紹了Go語言定時(shí)任務(wù)的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05Golang10進(jìn)制轉(zhuǎn)16進(jìn)制的幾種方法代碼示例
這篇文章主要給大家介紹了關(guān)于Golang10進(jìn)制轉(zhuǎn)16進(jìn)制的幾種方法,進(jìn)制轉(zhuǎn)換是Golang的一些基本操作,文中通過實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07