亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Go語(yǔ)言的反射機(jī)制詳解

 更新時(shí)間:2022年07月18日 16:24:41   作者:奮斗的大橙子  
本文詳細(xì)講解了Go語(yǔ)言的反射機(jī)制,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

反射是語(yǔ)言里面是非常重要的一個(gè)特性,我們經(jīng)常會(huì)看見這個(gè)詞,但是對(duì)于反射沒(méi)有一個(gè)很好的理解,主要是因?yàn)閷?duì)于反射的使用場(chǎng)景不太熟悉。

一、理解變量的內(nèi)在機(jī)制

1.類型信息,元信息,是預(yù)先定義好的,靜態(tài)的。

2.值信息,程序進(jìn)行過(guò)程中,動(dòng)態(tài)變化的。

二、反射和空接口

1.空接口相當(dāng)于一個(gè)容器,能接受任何東西。

2.那怎么判斷空接口變量存儲(chǔ)的是什么類型呢?之前有使用過(guò)類型斷言,這只是一個(gè)比較基礎(chǔ)的方法

3.如果想獲取存儲(chǔ)變量的類型信息和值信息就要使用反射機(jī)制,所以反射是什么? 反射就是動(dòng)態(tài)的獲取變量類型信息和值信息的機(jī)制。

三、怎么利用反射分析空接口里面的信息呢?

①首先利用的是GO語(yǔ)言里面的Reflect包

②利用包里的TypeOf方法可以獲取變量的類型信息

func reflect_typeof(a interface{}) {
    t := reflect.TypeOf(a)
    fmt.Printf("type of a is:%v\n", t)
 
    k := t.Kind()
    switch k {
    case reflect.Int64:
        fmt.Printf("a is int64\n")
    case reflect.String:
        fmt.Printf("a is string\n")
    }
}

利用Kind() 可以獲取t的類型,如代碼所示,這里可以判斷a是Int64還是string, 像下面一樣使用:

func main() {
    var x int64 = 3
    reflect_example(x)
 
    var y string = "hello"
    reflect_example(y)
}

打印結(jié)果:

type of a is:int64
a is int64
type of a is:string
a is string

③利用包里的ValueOf方法可以獲取變量的值信息

func reflect_value(a interface{}) {
    v := reflect.ValueOf(a)
    k := v.Kind()
    switch k {
    case reflect.Int64:
        fmt.Printf("a is Int64, store value is:%d\n", v.Int())
    case reflect.String:
        fmt.Printf("a is String, store value is:%s\n", v.String())
    }
}

利用ValueOf方法可以得到變量的值信息,ValueOf返回的是一個(gè)Value結(jié)構(gòu)體類型,有趣的是 可以使用 v.Type() 獲取該變量的類型,和上面reflect.TypeOf() 獲取的結(jié)果一樣。

此外,因?yàn)橹敌畔⑹莿?dòng)態(tài)的,所以我們不僅僅可以獲取這個(gè)變量的類型,還能取得這個(gè)變量里面存儲(chǔ)的值,利用 v.Int() 、 v.String() 等等就能取得值。如上面的main,調(diào)用此函數(shù)返回的結(jié)果:

a is Int64, store value is:3
a is String, store value is:hello

這里存在一個(gè)問(wèn)題,如果傳進(jìn)去一個(gè)類型,使用了錯(cuò)誤的解析,那么將會(huì)在運(yùn)行的時(shí)候報(bào)錯(cuò), 例如將 一個(gè)string類型強(qiáng)行的v.Int()。

既然值類型是動(dòng)態(tài)的,能取到保存的值,同樣可以設(shè)置值。在反射里面有很多set的方法,例如SetFloat、SetInt()、SetString()等可以幫助我們?cè)O(shè)置值。

下面的例子,我想把 x設(shè)置為 6.28,但是會(huì)報(bào)錯(cuò)!

func main() {
    var x float64 = 3.14
    v := reflect.ValueOf(x)
    v.SetFloat(6.28)
    fmt.Printf("After Set Value is %f", x)
}

錯(cuò)誤結(jié)果:

panic: reflect: reflect.Value.SetFloat using unaddressable value
......

結(jié)果上說(shuō)明是不可設(shè)置的,為什么呢? 因?yàn)槲覀兊膞是一個(gè)值類型,而值類型的傳遞是拷貝了一個(gè)副本,當(dāng) v := reflect.ValueOf(x) 函數(shù)通過(guò)傳遞一個(gè) x 拷貝創(chuàng)建了 v,那么 v 的改變并不能更改原始的 x。要想 v 的更改能作用到 x,那就必須傳遞 x 的地址 v = reflect.ValueOf(&x)。修改程序如下:

func main() {
    var x float64 = 3.14
    v := reflect.ValueOf(&x)
    v.SetFloat(6.28)
    fmt.Printf("After Set Value is %f", x)
}

結(jié)果:依然報(bào)錯(cuò)!為什么傳了地址還報(bào)錯(cuò)?因?yàn)?amp;x是地址了,所以它的類型就變了,可以通過(guò)v.Type(),看下改變成了什么:

func main() {
    var x float64 = 3.14
    v := reflect.ValueOf(&x)
    fmt.Printf("type of v is %v", v.Type())   //打印的結(jié)果是:type of v is *float64
}

由程序可以知道,這個(gè)返回的是一個(gè)指針類型的。所以上面SetFloat才會(huì)失敗,那怎么做?

我們正常的賦值,如果是地址的話,例如下面:一般我們都會(huì)對(duì)*y進(jìn)行賦值, *的意思就是往這個(gè)地址里面賦值。

var y *float64 = new(float64)
*y = 10.12
fmt.Printf("y = %v", *y)

同樣的,我們?cè)诜瓷淅锩嬉部梢匀〉刂?,需要通過(guò) Elem() 方法進(jìn)行取地址。再次修改程序

func main() {
    var x float64 = 3.14
    v := reflect.ValueOf(&x)
    fmt.Printf("type of v is %v\n", v.Type())
    v.Elem().SetFloat(6.28)
    fmt.Printf("After set x is %v", x)
}

結(jié)果為:

type of v is *float64
After set x is 6.28

四、利用反射獲取結(jié)構(gòu)體里面的方法和調(diào)用。

1.獲取結(jié)構(gòu)體的字段

我們可以通過(guò)上面的方法判斷一個(gè)變量是不是結(jié)構(gòu)體。

可以通過(guò) NumField() 獲取所有結(jié)構(gòu)體字段的數(shù)目、進(jìn)而遍歷,通過(guò)Field()方法獲取字段的信息。

type Student struct {
    Name  string
    Sex   int
    Age   int
    Score float32
}
 
func main() {
    //創(chuàng)建一個(gè)結(jié)構(gòu)體變量
    var s Student = Student{
        Name:  "BigOrange",
        Sex:   1,
        Age:   10,
        Score: 80.1,
    }
 
    v := reflect.ValueOf(s)
    t := v.Type()
    kind := t.Kind()
     
    //分析s變量的類型,如果是結(jié)構(gòu)體類型,那么遍歷所有的字段
    switch kind {
    case reflect.Int64:
        fmt.Printf("s is int64\n")
    case reflect.Float32:
        fmt.Printf("s is int64\n")
    case reflect.Struct:
        fmt.Printf("s is struct\n")
        fmt.Printf("field num of s is %d\n", v.NumField())
        //NumFiled()獲取字段數(shù),v.Field(i)可以取得下標(biāo)位置的字段信息,返回的是一個(gè)Value類型的值
        for i := 0; i < v.NumField(); i++ {
            field := v.Field(i)
            //打印字段的名稱、類型以及值
            fmt.Printf("name:%s type:%v value:%v\n",
                t.Field(i).Name, field.Type().Kind(), field.Interface())
        }
    default:
        fmt.Printf("default\n")
    }
}

執(zhí)行結(jié)果:

s is struct
field num of s is 4
name:Name type:string value:BigOrange
name:Sex type:int value:1
name:Age type:int value:10
name:Score type:float32 value:80.1

這里需要說(shuō)明幾個(gè)問(wèn)題:

①打印字段名稱的時(shí)候,使用的是t.Field(i).Name ,Name是靜態(tài)的,所以屬于類型的信息

②打印值的時(shí)候,這里將field.Interface()實(shí)際上相當(dāng)于ValueOf的反操作(可以參考這篇文章http://chabaoo.cn/article/255856.htm),所以才能把值打印出來(lái)

③此外如果Student中的Name字段變?yōu)閚ame(私有),那么則會(huì)報(bào)錯(cuò),不能反射出私有變量 錯(cuò)誤信息 “panic: reflect.Value.Interface: cannot return value obtained from unexported field or method”

2.對(duì)結(jié)構(gòu)體內(nèi)的字段進(jìn)行賦值操作

參考下面的代碼,對(duì)上面的Student進(jìn)行賦值操作:

func main() {
    s := Student{
        Name:  "BigOrange",
        Sex:   1,
        Age:   10,
        Score: 80.1,
    }
 
    fmt.Printf("Name:%v, Sex:%v,Age:%v,Score:%v \n", s.Name, s.Sex, s.Age, s.Score)
    v := reflect.ValueOf(&s)  //這里傳的是地址?。。?
 
    v.Elem().Field(0).SetString("ChangeName")
    v.Elem().FieldByName("Score").SetFloat(99.9)
 
    fmt.Printf("Name:%v, Sex:%v,Age:%v,Score:%v \n", s.Name, s.Sex, s.Age, s.Score)
}

結(jié)果:

Name:BigOrange, Sex:1,Age:10,Score:80.1
Name:ChangeName, Sex:1,Age:10,Score:99.9

3.獲取結(jié)構(gòu)體里面的方法

可以通過(guò)NumMethod()獲得接頭體里面的方法數(shù)量,然后遍歷通過(guò)Method()獲取方法的具體信息。如下代碼所示:

//新增-設(shè)置名稱方法
func (s *Student) SetName(name string) {
     fmt.Printf("有參數(shù)方法 通過(guò)反射進(jìn)行調(diào)用:%v\n", s)
     s.Name = name
}
//新增-打印信息方法
func (s *Student) PrintStudent() {
    fmt.Printf("無(wú)參數(shù)方法 通過(guò)反射進(jìn)行調(diào)用:%v\n", s)
}
 
func main() {
    s := Student{
        Name:  "BigOrange",
        Sex:   1,
        Age:   10,
        Score: 80.1,
    }
 
    v := reflect.ValueOf(&s)
    //取得Type信息
    t := v.Type()
     
    fmt.Printf("struct student have %d methods\n", t.NumMethod())
 
    for i := 0; i < t.NumMethod(); i++ {
        method := t.Method(i)
        fmt.Printf("struct %d method, name:%s type:%v\n", i, method.Name, method.Type)
    }
}

輸出:

struct student have 2 methods
struct 0 method, name:PrintStudent type:func(*main.Student)
struct 1 method, name:SetName type:func(*main.Student, string)

從結(jié)果中看到我們可以獲取方法的名稱以及簽名信息,并且這個(gè)方法的輸出順序是按照字母排列的。

并且輸出結(jié)果可以看到一個(gè)有趣的現(xiàn)象:結(jié)構(gòu)體的方法其實(shí)也是通過(guò)函數(shù)實(shí)現(xiàn)的例如 func(s Student) SetName(name string) 這個(gè)方法,反射之后的結(jié)果就是 func(main.Student , string) 實(shí)際上把Student當(dāng)參數(shù)了。

此外還可以通過(guò)反射來(lái)調(diào)用這些方法。想要通過(guò)反射調(diào)用結(jié)構(gòu)體里面的方法,首先要知道方法調(diào)用時(shí)一個(gè)動(dòng)態(tài)的,所以要先通過(guò)ValueOf獲取值,然后通過(guò)獲取的值進(jìn)行方法的調(diào)用 ,通過(guò) value里面的Method方法 返回一個(gè)方法,然后通過(guò)Call方法調(diào)用,Call是參數(shù)是一個(gè)切片,也就是參數(shù)的列表。以下是Call方法的定義:可以看到參數(shù)是一個(gè)Value的數(shù)組:

如下代碼展示了如何調(diào)用有參數(shù)的方法和無(wú)參數(shù)的方法:

func main() {
    s := Student{
        Name:  "BigOrange",
        Sex:   1,
        Age:   10,
        Score: 80.1,
    }
 
    v := reflect.ValueOf(&s)
 
    //通過(guò)reflect.Value獲取對(duì)應(yīng)的方法并調(diào)用
    m1 := v.MethodByName("PrintStudent")
    var args []reflect.Value
    m1.Call(args)
 
    m2 := v.MethodByName("SetName")
    var args2 []reflect.Value
    name := "stu01"
    nameVal := reflect.ValueOf(name)
    args2 = append(args2, nameVal)
    m2.Call(args2)
    m1.Call(args)
}

執(zhí)行結(jié)果:

無(wú)參數(shù)方法 通過(guò)反射進(jìn)行調(diào)用:&main.Student{Name:"BigOrange", Sex:1, Age:10, Score:80.1}
有參數(shù)方法 通過(guò)反射進(jìn)行調(diào)用:&main.Student{Name:"BigOrange", Sex:1, Age:10, Score:80.1}
無(wú)參數(shù)方法 通過(guò)反射進(jìn)行調(diào)用:&main.Student{Name:"stu01", Sex:1, Age:10, Score:80.1}

上面格式打?。?/p>

  • %v 相應(yīng)值的默認(rèn)格式。 Printf("%v", people) {zhangsan},
  • %+v 打印結(jié)構(gòu)體時(shí),會(huì)添加字段名 Printf("%+v", people) {Name:zhangsan}
  • %#v 相應(yīng)值的Go語(yǔ)法表示 Printf("#v", people) main.Human{Name:"zhangsan"}

五、怎么獲取結(jié)構(gòu)體里tag的信息。

有時(shí)候我們?cè)陬愋蜕厦娑x一些tag,例如使用json和數(shù)據(jù)庫(kù)的時(shí)候。Field()方法返回的StructField結(jié)構(gòu)體中保存著Tag信息,并且Tag信息可以通過(guò)一個(gè)Get(Key)的方法獲取出來(lái),如下代碼所示:

type Student struct {
    Name string `json:"jsName" db:"dbName"`
}
 
func main() {
    s := Student{
        Name: "BigOrange",
    }
    v := reflect.ValueOf(&s)
    t := v.Type()
    field0 := t.Elem().Field(0)
    fmt.Printf("tag json=%s\n", field0.Tag.Get("json"))
    fmt.Printf("tag db=%s\n", field0.Tag.Get("db"))
}

結(jié)果:

tag json=jsName
tag db=dbName

六、應(yīng)用場(chǎng)景

1.序列化和反序列化,比如json, protobuf等各種數(shù)據(jù)協(xié)議

2.各種數(shù)據(jù)庫(kù)的ORM,比如gorm,sqlx等數(shù)據(jù)庫(kù)中間件

3.配置文件解析相關(guān)的庫(kù),比如yaml、ini等

到此這篇關(guān)于Go語(yǔ)言反射機(jī)制的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Go語(yǔ)言實(shí)現(xiàn)關(guān)閉http請(qǐng)求的方式總結(jié)

    Go語(yǔ)言實(shí)現(xiàn)關(guān)閉http請(qǐng)求的方式總結(jié)

    面試的時(shí)候問(wèn)到如何關(guān)閉http請(qǐng)求,一般人脫口而出的是關(guān)閉response.body,這是錯(cuò)誤的。本文為大家整理了三個(gè)正確關(guān)閉http請(qǐng)求的方法,希望對(duì)大家有所幫助
    2023-02-02
  • golang生成JSON以及解析JSON

    golang生成JSON以及解析JSON

    這篇文章主要介紹了golang生成JSON以及解析JSON,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • go語(yǔ)言中的協(xié)程詳解

    go語(yǔ)言中的協(xié)程詳解

    本文詳細(xì)講解了go語(yǔ)言中的協(xié)程,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-07-07
  • Go Module常用命令及如何使用Go Module

    Go Module常用命令及如何使用Go Module

    go module是go官方自帶的go依賴管理庫(kù),在1.13版本正式推薦使用,這篇文章主要介紹了Go Module常用命令及如何使用Go Module,需要的朋友可以參考下
    2024-02-02
  • Golang字符串變位詞示例詳解

    Golang字符串變位詞示例詳解

    這篇文章主要給大家介紹了關(guān)于GoLang字符串變位詞的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-10-10
  • Go語(yǔ)言?Channel通道詳解

    Go語(yǔ)言?Channel通道詳解

    Channel是一個(gè)通道,可以通過(guò)它讀取和寫入數(shù)據(jù),它就像水管一樣,網(wǎng)絡(luò)數(shù)據(jù)通過(guò)Channel 讀取和寫入,這篇文章主要給大家介紹了關(guān)于Go語(yǔ)言?Channel通道的相關(guān)資料,需要的朋友可以參考下
    2023-07-07
  • Go語(yǔ)言排序算法:快速、可靠的排序解決方案

    Go語(yǔ)言排序算法:快速、可靠的排序解決方案

    Go語(yǔ)言提供了多種快速、可靠的排序算法,可以滿足不同場(chǎng)景下的排序需求,其中最常用的排序算法包括快速排序、歸并排序和堆排序,需要的朋友可以參考下
    2023-10-10
  • Golang中的path/filepath包用法

    Golang中的path/filepath包用法

    這篇文章主要介紹了Golang中的path/filepath包用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12
  • Golang中slice切片的實(shí)現(xiàn)示例

    Golang中slice切片的實(shí)現(xiàn)示例

    Go語(yǔ)言中,切片是對(duì)數(shù)組的抽象,提供了更靈活的動(dòng)態(tài)數(shù)組解決方案,本文就來(lái)介紹一下Golang中slice切片的實(shí)現(xiàn)示例,感興趣的可以了解一下
    2024-09-09
  • 使用go實(shí)現(xiàn)一個(gè)超級(jí)mini的消息隊(duì)列的示例代碼

    使用go實(shí)現(xiàn)一個(gè)超級(jí)mini的消息隊(duì)列的示例代碼

    本文主要介紹了使用go實(shí)現(xiàn)一個(gè)超級(jí)mini的消息隊(duì)列的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-12-12

最新評(píng)論