一文帶你掌握Golang的反射基礎(chǔ)
什么是反射
反射的機制是在運行時可以獲取到其變量的類型和值,且可以在運行時對其變量類型和值進行檢查,可以對其值進行修改。這種機制,其實在編寫業(yè)務(wù)代碼時是比較少用到的,那么在框架中,使用的反射的機制是比較常見,如web框架、Orm框架,實現(xiàn)通用性的目的。
go的反射
go的反射是由其標準庫中的reflect包實現(xiàn),該reflect包實現(xiàn)了在運行時進行反射的能力,本篇先講解一下reflect的常用的幾個方法,下篇模擬一些貼近實際的應(yīng)用場景來剖析到底怎么用。
實戰(zhàn)reflect包中常用的幾個類型和方法
1.通過反射獲取變量的類型(Type)
package?main import?( ?"fmt" ?"reflect" ) func?main()?{ ?fmt.Println("公眾號搜索:不背鍋運維") ?a?:=?100.89 ?b?:=?"hello" ?//獲取變量a和b的類型,即使用reflect.TypeOf反射出了基本數(shù)據(jù)類型的變量的類型。 ?fmt.Println(reflect.TypeOf(a))?//float64 ?fmt.Println(reflect.TypeOf(b))?//string ?//?反射獲取結(jié)構(gòu)體類型信息 ?type?User?struct?{ ??Name?string ??Age??int ?} ?u?:=?User{"tantianran",?18} ?fmt.Println(reflect.TypeOf(u))?//main.User }
2.使用反射獲取變量對應(yīng)的值(Value)
package?main import?( ?"fmt" ?"reflect" ) func?main()?{ ?fmt.Println("公眾號搜索:不背鍋運維") ?a?:=?100.89 ?b?:=?"hello" ?//使用反射獲取變量的Value。 ?fmt.Println(reflect.ValueOf(a))?//100.89 ?fmt.Println(reflect.ValueOf(b))?//hello ?//?使用反射獲取結(jié)構(gòu)體變量的Value ?type?User?struct?{ ??Name?string ??Age??int ?} ?u?:=?User{"tantianran",?18} ?fmt.Println(reflect.ValueOf(u))?//{tantianran?18} }
3.TypeOf()方法獲取到變量的類型后,再調(diào)用它的Kind()方法可以將返回的結(jié)果用來進行類型的判斷
package?main import?( ?"fmt" ?"reflect" ) func?main()?{ ?fmt.Println("公眾號搜索:不背鍋運維") ?a?:=?100.89 ?//通過反射獲取變量a的類型 ?aType?:=?reflect.TypeOf(a) ?fmt.Println(aType)?//float64 ?//返回變量a的Type后,調(diào)用它的Kind()方法獲取該類型的詳細信息 ?aKind?:=?aType.Kind() ?fmt.Println(aKind)?//float64 ?//?Kind()方法返回的結(jié)果主要用途是可以用來進行類型判斷 ?if?aKind?==?reflect.Float64?{?//?使用reflect包里面的數(shù)據(jù)類型Float64 ??fmt.Println("is?float64") ?}?else?{ ??fmt.Println("no") ?} }
注意,TypeOf()方法是不支持進行類型判斷的,如if aType == reflect.Float64,此時會報錯。
4.通過Elem()方法獲取變量的指針所指向的對象
注意,先傳入變量的地址給ValueOf方法,然后調(diào)用Elem()方法獲取該變量的指針所指向的對象
package?main import?( ?"fmt" ?"reflect" ) func?main()?{ ?fmt.Println("公眾號搜索:不背鍋運維") ?a?:=?100.89 ?//?ValueOf傳入的是地址,調(diào)用Elem()方法可以獲取變量的指針指向的對象 ?PaValue?:=?reflect.ValueOf(&a)?//傳入變量a的地址 ?fmt.Println(PaValue)?//0xc0000180a0 ?aElem?:=?PaValue.Elem() ?fmt.Println(aElem)??//100.89 }
5.通過反射修改變量的值
方式一:使用具體類型的設(shè)置值的方法,如SetInt,SetString等
package?main import?( ?"fmt" ?"log" ?"reflect" ) func?main()?{ ?fmt.Println("公眾號搜索:不背鍋運維") ?//聲明int類型變量和賦值 ?var?x?int?=?100 ?fmt.Println("修改之前?",?x) ?//通過反射,傳入變量的地址,并通過Elem方法獲取指向該變量地址的值 ?v?:=?reflect.ValueOf(&x).Elem() ?//判斷是否可以修改變量的值,使用CanSet方法獲取是否可以修改該變量的值,可以是true,反之false ?if?v.CanSet()?{ ??v.SetInt(89) ?}?else?{ ??log.Println("變量的值不可修改") ?} ?fmt.Println("修改之后",?x) }
方式2:使用Set方法
package?main import?( ?"fmt" ?"log" ?"reflect" ) func?main()?{ ?fmt.Println("公眾號搜索:不背鍋運維") ?//聲明int類型變量和賦值 ?var?x?int?=?100 ?fmt.Println("修改之前?",?x) ?//通過反射,傳入變量的地址,并通過Elem方法獲取指向該變量地址的值 ?v?:=?reflect.ValueOf(&x).Elem() ?//判斷是否可以修改變量的值,使用CanSet方法獲取是否可以修改該變量的值,可以是true,反之false ?if?v.CanSet()?{ ??a?:=?reflect.ValueOf(67)?//使用Set方法修改值,Set方法接收的是ValueOf的返回值 ??v.Set(a) ?}?else?{ ??log.Println("變量的值不可修改") ?} ?fmt.Println("修改之后",?x) }
注意:想要通過反射修改變量的值,那么必須在reflect.ValueOf傳入變量的地址,而不能傳入變量的值。
6.通過反射獲取結(jié)構(gòu)體變量的類型和值
package?main import?( ?"fmt" ?"reflect" ) type?User?struct?{ ?Name?string ?Age??int } func?(u?User)?show()?{ ?fmt.Println("Type,?",?reflect.TypeOf(u)) ?fmt.Println("Value,?",?reflect.ValueOf(u)) } func?main()?{ ?fmt.Println("公眾號搜索:不背鍋運維") ?u?:=?User{Name:?"ttr",?Age:?18} ?u.show() }
7.對比一下反射中的方法TypeOf()和Kind()輸出的區(qū)別
package?main import?( ?"fmt" ?"reflect" ) type?User?struct?{ ?Name?string ?Age??int } func?(u?User)?show()?{ ?uType?:=?reflect.TypeOf(u) ?fmt.Println("Type,?",?uType) ?uKind?:=?uType.Kind() ?fmt.Println("Kind?,?",?uKind) } func?main()?{ ?fmt.Println("公眾號搜索:不背鍋運維") ?u?:=?User{Name:?"ttr",?Age:?18} ?u.show() }
輸出:
Type, main.User
Kind , struct
main.User表示是哪個結(jié)構(gòu)體類型,struct表示是屬于哪一類數(shù)據(jù)類型
package?main import?( ?"fmt" ?"reflect" ) type?User?struct?{ ?Name?string ?Age??int } func?show(i?interface{})?{ ?iType?:=?reflect.TypeOf(i) ?iKind?:=?iType.Kind() ?if?iKind?!=?reflect.Struct?{ ??panic("Not?a?structure?type") ?}?else?{ ??val?:=?reflect.ValueOf(i) ??for?i?:=?0;?i?<?iType.NumField();?i++?{ ???fmt.Println("FiledName:",?iType.Field(i).Name,?"FiledType:",?iType.Field(i).Type,?"Value:",?val.Field(i)) ??} ?} } func?main()?{ ?fmt.Println("公眾號搜索:不背鍋運維") ? ?u?:=?User{Name:?"ttr",?Age:?18} ?show(u) }
輸出:
FiledName: Name FiledType: string Value: ttr
FiledName: Age FiledType: int Value: 18
NumField()方法獲取結(jié)構(gòu)體內(nèi)的字段數(shù)量,iType.Field(i).Name獲取結(jié)構(gòu)體字段名稱,iType.Field(i).Type獲取結(jié)構(gòu)體字段類型,val.Field(i))獲取結(jié)構(gòu)體字段中的值。
到此這篇關(guān)于一文帶你掌握Golang的反射基礎(chǔ)的文章就介紹到這了,更多相關(guān)Golang反射內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
go+redis實現(xiàn)消息隊列發(fā)布與訂閱的詳細過程
這篇文章主要介紹了go+redis實現(xiàn)消息隊列發(fā)布與訂閱,redis做消息隊列的缺點:沒有持久化,一旦消息沒有人消費,積累到一定程度后就會丟失,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-09-09Golang回調(diào)函數(shù)與閉包和接口函數(shù)的定義及使用介紹
這篇文章主要介紹了Golang回調(diào)函數(shù)與閉包和接口函數(shù)的定義及使用,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2023-05-05詳解Go語言中用 os/exec 執(zhí)行命令的五種方法
這篇文章主要介紹了Go語言中用 os/exec 執(zhí)行命令的五種方法,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-11-11