日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

反射(reflect)机制

發(fā)布時(shí)間:2025/3/21 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 反射(reflect)机制 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

什么是反射

官方對(duì)此有個(gè)非常簡(jiǎn)明的介紹,兩句話耐人尋味:

  • 反射提供一種讓程序檢查自身結(jié)構(gòu)的能力
  • 反射是困惑的源泉
  • 要深刻理解反射,個(gè)人感覺需要花時(shí)間在官方博客上再加以練習(xí),循序漸進(jìn),慢慢體會(huì)。

    反射的三個(gè)定律

    反射就是檢查interface的(value, type)對(duì)的,因?yàn)槿魏晤愋偷淖兞炕蚍椒ǘ际菍?shí)現(xiàn)了空接口。具體一點(diǎn)說就是Go提供一組方法提取interface的value,提供另一組方法提取interface的type.

    官方提供了三條定律來說明反射,比較清晰,下面也按照這三定律來總結(jié)。

    反射包里有兩個(gè)接口類型要先了解一下.

    • reflect.Type 提供一組接口處理interface的類型,即(value, type)中的type
    • reflect.Value提供一組接口處理interface的值,即(value, type)中的value

    下面會(huì)提到反射對(duì)象,所謂反射對(duì)象即反射包里提供的兩種類型的對(duì)象。

    • reflect.Type 類型對(duì)象
    • reflect.Value類型對(duì)象

    1. 反射的第一定律:反射可以將interface類型變量轉(zhuǎn)換成為reflect類型變量

    這里的reflect類型指的是reflect.Type和reflect.Value類型的變量

    package mainimport ("fmt""reflect" )func main() {var x float64 = 8.5t := reflect.TypeOf(x) // 這里的t類型為:reflect.Typefmt.Println("type:", t)v := reflect.ValueOf(x) // 這里的v類型為:reflect.Valuefmt.Println("value:", v) }

    2. 反射第二定律:反射可以將reflect類型對(duì)象還原成interface類型對(duì)象

    package mainimport ("fmt""reflect" )func main() {var x float64 = 8.5v := reflect.ValueOf(x) //這里v的類型為:reflect.Valuevar y float64 = v.Interface().(float64) //v通過Interface()函數(shù)將反射類型轉(zhuǎn)換為interface類型變量,再通過斷言為float64獲取值fmt.Println("value:", y) }

    3. 反射第三定律:如果要修改reflect類型對(duì)象,則value必須是可設(shè)置的

    package mainimport ("reflect""fmt" )func main() {var x float64 = 8.5fmt.Println("x :", x)v := reflect.ValueOf(&x)fmt.Println("settability of v:", v.CanSet())//v.SetFloat(5.8) // 這里會(huì)報(bào)panic的錯(cuò)誤,因?yàn)檫@時(shí)候v是不可設(shè)置的fmt.Println("settability of v:", v.Elem().CanSet())v.Elem().SetFloat(5.8)fmt.Println("x :", v.Elem().Interface().(float64)) }

    上面11行v代表的是指針地址,我們要設(shè)置的是指針?biāo)赶虻膬?nèi)容,也即我們想要修改的是*v。 那怎么通過v修改x的值呢?

    reflect.Value提供了Elem()方法,可以獲得指針向指向的value,也就是第14行和第15行的操作。

    反射的使用

    1. 查看結(jié)構(gòu)體類型、字段、方法、匿名字段、字段tag

    package mainimport ("fmt""reflect" )//定義結(jié)構(gòu)體 type User struct {Id intName string Age int }type Boy struct {UserAddr string `db:"addr"` }//綁定方法 func (u User) Say() {fmt.Println("Hello") }func (u User) Eat() {fmt.Println("Eat price") }func PrintInfo(i interface{}){t := reflect.TypeOf(i)fmt.Println("結(jié)構(gòu)體類型:", t)v := reflect.ValueOf(i)fmt.Println("結(jié)構(gòu)體的值:", v)fmt.Println("\n結(jié)構(gòu)體字段名稱 :字段類型 \t : 值 \t\t : 是否為匿名字段: 字段tag為db的值")for i := 0; i < t.NumField(); i++ {f := t.Field(i)val := v.Field(i)fmt.Printf("%s\t\t : %v\t : %v\t : %v\t : %v\n", f.Name, f.Type, val.Interface(), f.Anonymous, f.Tag.Get("db"))//fieldByName, _ := t.FieldByName(f.Name)//fmt.Println(fieldByName)}fmt.Println("\n結(jié)構(gòu)體綁定的方法:方法類型\t:方法地址 \t\t 方法索引 ")for i := 0; i < t.NumMethod(); i++ {m := t.Method(i)fmt.Printf("%s\t\t %v\t %v\t %v\n", m.Name, m.Type, &m.Func, m.Index)//Func, _ := t.MethodByName(m.Name)//fmt.Println(Func)} }func main() {b := Boy{User: User{Id: 1,Name: "Yuan",Age: 26,},Addr: "chengdu",}PrintInfo(b) }

    2. 修改結(jié)構(gòu)體字段的值

    package mainimport ("fmt""reflect" )//定義結(jié)構(gòu)體 type User struct {Id intName stringAge int }func SetValue(i interface{}){v := reflect.ValueOf(i)//獲取指針指向的元素elem:= v.Elem()//獲取要修改的字段name := elem.FieldByName("Name")if name.Kind() == reflect.String && name.CanSet(){name.SetString("老大")} }func main() {u := User{Id: 1,Name: "Yuan",Age: 26,}fmt.Println("修改前:", u)SetValue(&u)fmt.Println("修改后:", u) }

    3. 調(diào)用結(jié)構(gòu)體綁定的方法

    package mainimport ("fmt""reflect" )//定義結(jié)構(gòu)體 type User struct {Id intName stringAge int }//綁定方法 func (u User) Say(s string) {fmt.Println("Hello", s) }func (u User) Eat() {fmt.Println("Eat price.") }func CallFunc(i interface{}){v := reflect.ValueOf(i)//獲取結(jié)構(gòu)體綁定的方法sayFunc := v.MethodByName("Say")//構(gòu)建調(diào)用參數(shù)args := []reflect.Value{reflect.ValueOf("world!")}//調(diào)用方法sayFunc.Call(args)//調(diào)用無參方法eatFunc := v.MethodByName("Eat")args = []reflect.Value{}eatFunc.Call(args) //這里必須傳值,不能為空 }func main() {u := User{Id: 1,Name: "Yuan",Age: 26,}CallFunc(&u) }

    反射的牛刀小試

    在Kubernetes中的資源配置文件的主要形式是yaml格式的,同時(shí)Kubernetes還支持json格式和proto格式的配置文件,下面我們自己可以實(shí)現(xiàn)一個(gè)簡(jiǎn)單的ini格式配置文件的解析,使用案例如下:

    package mainfunc main() {// 制作測(cè)試數(shù)據(jù)var conf Configconf.ServerConf.IP = "192.168.0.1"conf.ServerConf.Port = 8080conf.ClientConf.Username = "Yuan"conf.ClientConf.Password = "Abcd123456"//將結(jié)構(gòu)體信息寫入配置文件StructToFile("./config.ini", conf)var config Config// 從配置文件解析到結(jié)構(gòu)體中FileToStruct("./config.ini", &config)logger.Printf("%#v", config) }

    執(zhí)行上面的代碼會(huì)生成config.ini配置文件并輸出從配置文件解析的結(jié)構(gòu)體信息到控制臺(tái)

    其中生成的config.in文件內(nèi)容如下:

    [SERVER] ip = 192.168.0.1 port = 8080[CLIENT] username = Yuan password = Abcd123456

    控制臺(tái)輸出為:

    main.go:18: main.Config{ServerConf:main.ServerConfig{IP:"192.168.0.1", Port:8080}, ClientConf:main.ClientConfig{Username:"Yuan", Password:"Abcd123456"}}

    項(xiàng)目源碼地址

    總結(jié)

    以上是生活随笔為你收集整理的反射(reflect)机制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。