一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

腳本之家,腳本語言編程技術(shù)及教程分享平臺!
分類導(dǎo)航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務(wù)器之家 - 腳本之家 - Golang - 學(xué)習(xí)使用Go反射的用法示例

學(xué)習(xí)使用Go反射的用法示例

2020-06-02 10:42網(wǎng)管叨bi叨 Golang

這篇文章主要介紹了學(xué)習(xí)使用Go反射的用法示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

什么是反射

大多數(shù)時候,Go中的變量,類型和函數(shù)非常簡單直接。當(dāng)需要一個類型、變量或者是函數(shù)時,可以直接定義它們:

?
1
2
3
4
5
6
7
8
9
10
type Foo struct {
 A int
 B string
}
 
var x Foo
 
func DoSomething(f Foo) {
 fmt.Println(f.A, f.B)
}

但是有時你希望在運(yùn)行時使用變量的在編寫程序時還不存在的信息。比如你正在嘗試將文件或網(wǎng)絡(luò)請求中的數(shù)據(jù)映射到變量中。或者你想構(gòu)建一個適用于不同類型的工具。在這種情況下,你需要使用反射。反射使您能夠在運(yùn)行時檢查類型。它還允許您在運(yùn)行時檢查,修改和創(chuàng)建變量,函數(shù)和結(jié)構(gòu)體。

Go中的反射是基于三個概念構(gòu)建的:類型,種類和值(Types Kinds Values)。標(biāo)準(zhǔn)庫中的reflect包提供了 Go 反射的實(shí)現(xiàn)。

反射變量類型

首先讓我們看一下類型。你可以使用反射來調(diào)用函數(shù)varType := reflect.TypeOf(var)來獲取變量var的類型。這將返回類型為reflect.Type的變量,該變量具有獲取定義時變量的類型的各種信息的方法集。下面我們來看一下常用的獲取類型信息的方法。

我們要看的第一個方法是Name()。這將返回變量類型的名稱。某些類型(例如切片或指針)沒有名稱,此方法會返回空字符串。

下一個方法,也是我認(rèn)為第一個真正非常有用的方法是Kind()。Type是由Kind組成的---Kind 是切片,映射,指針,結(jié)構(gòu),接口,字符串,數(shù)組,函數(shù),int或其他某種原始類型的抽象表示。要理解Type和Kind之間的差異可能有些棘手,但是請你以這種方式來思考。如果定義一個名為Foo的結(jié)構(gòu)體,則Kind為struct,類型為Foo。

使用反射時要注意的一件事:反射包中的所有內(nèi)容都假定你知道自己在做什么,并且如果使用不正確,許多函數(shù)和方法調(diào)用都會引起 panic。例如,如果你在reflect.Type上調(diào)用與當(dāng)前類型不同的類型關(guān)聯(lián)的方法,您的代碼將會panic。

如果變量是指針,映射,切片,通道或數(shù)組變量,則可以使用varType.Elem()找出指向或包含的值的類型。

如果變量是結(jié)構(gòu)體,則可以使用反射來獲取結(jié)構(gòu)體中的字段數(shù),并從每個字段上獲取reflect.StructField結(jié)構(gòu)體。 reflection.StructField為您提供了字段的名稱,標(biāo)號,類型和結(jié)構(gòu)體標(biāo)簽。其中標(biāo)簽信息對應(yīng)reflect.StructTag類型的字符串,并且它提供了Get方法用于解析和根據(jù)特定key提取標(biāo)簽信息中的子串。

下面是一個簡單的示例,用于輸出各種變量的類型信息:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
type Foo struct {
  A int `tag1:"First Tag" tag2:"Second Tag"`
  B string
}
 
func main() {
  sl := []int{1, 2, 3}
  greeting := "hello"
  greetingPtr := &greeting
  f := Foo{A: 10, B: "Salutations"}
  fp := &f
 
  slType := reflect.TypeOf(sl)
  gType := reflect.TypeOf(greeting)
  grpType := reflect.TypeOf(greetingPtr)
  fType := reflect.TypeOf(f)
  fpType := reflect.TypeOf(fp)
 
  examiner(slType, 0)
  examiner(gType, 0)
  examiner(grpType, 0)
  examiner(fType, 0)
  examiner(fpType, 0)
}
 
func examiner(t reflect.Type, depth int) {
  fmt.Println(strings.Repeat("\t", depth), "Type is", t.Name(), "and kind is", t.Kind())
  switch t.Kind() {
  case reflect.Array, reflect.Chan, reflect.Map, reflect.Ptr, reflect.Slice:
    fmt.Println(strings.Repeat("\t", depth+1), "Contained type:")
    examiner(t.Elem(), depth+1)
  case reflect.Struct:
    for i := 0; i < t.NumField(); i++ {
      f := t.Field(i)
      fmt.Println(strings.Repeat("\t", depth+1), "Field", i+1, "name is", f.Name, "type is", f.Type.Name(), "and kind is", f.Type.Kind())
      if f.Tag != "" {
        fmt.Println(strings.Repeat("\t", depth+2), "Tag is", f.Tag)
        fmt.Println(strings.Repeat("\t", depth+2), "tag1 is", f.Tag.Get("tag1"), "tag2 is", f.Tag.Get("tag2"))
      }
    }
  }
}

變量的類型輸出如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Type is and kind is slice
   Contained type:
   Type is int and kind is int
 Type is string and kind is string
 Type is and kind is ptr
   Contained type:
   Type is string and kind is string
 Type is Foo and kind is struct
   Field 1 name is A type is int and kind is int
     Tag is tag1:"First Tag" tag2:"Second Tag"
     tag1 is First Tag tag2 is Second Tag
   Field 2 name is B type is string and kind is string
 Type is and kind is ptr
   Contained type:
   Type is Foo and kind is struct
     Field 1 name is A type is int and kind is int
       Tag is tag1:"First Tag" tag2:"Second Tag"
       tag1 is First Tag tag2 is Second Tag
     Field 2 name is B type is string and kind is string

Run in go playground: https://play.golang.org/p/lZ97yAUHxX

使用反射創(chuàng)建新實(shí)例

除了檢查變量的類型外,還可以使用反射來讀取,設(shè)置或創(chuàng)建值。首先,需要使用refVal := reflect.ValueOf(var) 為變量創(chuàng)建一個reflect.Value實(shí)例。如果希望能夠使用反射來修改值,則必須使用refPtrVal := reflect.ValueOf(&var);獲得指向變量的指針。如果不這樣做,則可以使用反射來讀取該值,但不能對其進(jìn)行修改。

一旦有了reflect.Value實(shí)例就可以使用Type()方法獲取變量的reflect.Type。

如果要修改值,請記住它必須是一個指針,并且必須首先對其進(jìn)行解引用。使用refPtrVal.Elem().Set(newRefVal)來修改值,并且傳遞給Set()的值也必須是reflect.Value。

如果要創(chuàng)建一個新值,可以使用函數(shù)newPtrVal := reflect.New(varType)來實(shí)現(xiàn),并傳入一個reflect.Type。這將返回一個指針值,然后可以像上面那樣使用Elem().Set()對其進(jìn)行修改。

最后,你可以通過調(diào)用Interface()方法從reflect.Value回到普通變量值。由于Go沒有泛型,因此變量的原始類型會丟失;該方法返回類型為interface{}的值。如果創(chuàng)建了一個指針以便可以修改該值,則需要使用Elem().Interface()解引用反射的指針。在這兩種情況下,都需要將空接口轉(zhuǎn)換為實(shí)際類型才能使用它。

下面的代碼來演示這些概念:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
type Foo struct {
  A int `tag1:"First Tag" tag2:"Second Tag"`
  B string
}
 
func main() {
  greeting := "hello"
  f := Foo{A: 10, B: "Salutations"}
 
  gVal := reflect.ValueOf(greeting)
  // not a pointer so all we can do is read it
  fmt.Println(gVal.Interface())
 
  gpVal := reflect.ValueOf(&greeting)
  // it's a pointer, so we can change it, and it changes the underlying variable
  gpVal.Elem().SetString("goodbye")
  fmt.Println(greeting)
 
  fType := reflect.TypeOf(f)
  fVal := reflect.New(fType)
  fVal.Elem().Field(0).SetInt(20)
  fVal.Elem().Field(1).SetString("Greetings")
  f2 := fVal.Elem().Interface().(Foo)
  fmt.Printf("%+v, %d, %s\n", f2, f2.A, f2.B)
}

他們的輸出如下:

?
1
2
3
hello
goodbye
{A:20 B:Greetings}, 20, Greetings

Run in go playground https://play.golang.org/p/PFcEYfZqZ8

反射創(chuàng)建引用類型的實(shí)例

除了生成內(nèi)置類型和用戶定義類型的實(shí)例之外,還可以使用反射來生成通常需要make函數(shù)的實(shí)例。可以使用reflect.MakeSlice,reflect.MakeMap和reflect.MakeChan函數(shù)制作切片,Map或通道。在所有情況下,都提供一個reflect.Type,然后獲取一個reflect.Value,可以使用反射對其進(jìn)行操作,或者可以將其分配回一個標(biāo)準(zhǔn)變量。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
func main() {
 // 定義變量
  intSlice := make([]int, 0)
  mapStringInt := make(map[string]int)
 
 // 獲取變量的 reflect.Type
  sliceType := reflect.TypeOf(intSlice)
  mapType := reflect.TypeOf(mapStringInt)
 
  // 使用反射創(chuàng)建類型的新實(shí)例
  intSliceReflect := reflect.MakeSlice(sliceType, 0, 0)
  mapReflect := reflect.MakeMap(mapType)
 
  // 將創(chuàng)建的新實(shí)例分配回一個標(biāo)準(zhǔn)變量
  v := 10
  rv := reflect.ValueOf(v)
  intSliceReflect = reflect.Append(intSliceReflect, rv)
  intSlice2 := intSliceReflect.Interface().([]int)
  fmt.Println(intSlice2)
 
  k := "hello"
  rk := reflect.ValueOf(k)
  mapReflect.SetMapIndex(rk, rv)
  mapStringInt2 := mapReflect.Interface().(map[string]int)
  fmt.Println(mapStringInt2)
}

使用反射創(chuàng)建函數(shù)

反射不僅僅可以為存儲數(shù)據(jù)創(chuàng)造新的地方。還可以使用reflect.MakeFunc函數(shù)使用reflect來創(chuàng)建新函數(shù)。該函數(shù)期望我們要創(chuàng)建的函數(shù)的reflect.Type,以及一個閉包,其輸入?yún)?shù)為[]reflect.Value類型,其返回類型也為[] reflect.Value類型。下面是一個簡單的示例,它為傳遞給它的任何函數(shù)創(chuàng)建一個定時包裝器:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
func MakeTimedFunction(f interface{}) interface{} {
  rf := reflect.TypeOf(f)
  if rf.Kind() != reflect.Func {
    panic("expects a function")
  }
  vf := reflect.ValueOf(f)
  wrapperF := reflect.MakeFunc(rf, func(in []reflect.Value) []reflect.Value {
    start := time.Now()
    out := vf.Call(in)
    end := time.Now()
    fmt.Printf("calling %s took %v\n", runtime.FuncForPC(vf.Pointer()).Name(), end.Sub(start))
    return out
  })
  return wrapperF.Interface()
}
 
func timeMe() {
  fmt.Println("starting")
  time.Sleep(1 * time.Second)
  fmt.Println("ending")
}
 
func timeMeToo(a int) int {
  fmt.Println("starting")
  time.Sleep(time.Duration(a) * time.Second)
  result := a * 2
  fmt.Println("ending")
  return result
}
 
func main() {
  timed := MakeTimedFunction(timeMe).(func())
  timed()
  timedToo := MakeTimedFunction(timeMeToo).(func(int) int)
  fmt.Println(timedToo(2))
}

你可以在goplayground運(yùn)行代碼https://play.golang.org/p/QZ8ttFZzGx并看到輸出如下:

?
1
2
3
4
5
6
7
starting
ending
calling main.timeMe took 1s
starting
ending
calling main.timeMeToo took 2s
4

反射是每個Go開發(fā)人員都應(yīng)了解并學(xué)會的強(qiáng)大工具。但是使用他們可以用來做什么呢?在下一篇博客文章中,我將探討現(xiàn)有庫中反射的一些用法,并使用反射來創(chuàng)建一些新的東西。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。

原文鏈接:https://segmentfault.com/a/1190000021619810

延伸 · 閱讀

精彩推薦
  • GolangGolang中Bit數(shù)組的實(shí)現(xiàn)方式

    Golang中Bit數(shù)組的實(shí)現(xiàn)方式

    這篇文章主要介紹了Golang中Bit數(shù)組的實(shí)現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    天易獨(dú)尊11682021-06-09
  • Golanggolang json.Marshal 特殊html字符被轉(zhuǎn)義的解決方法

    golang json.Marshal 特殊html字符被轉(zhuǎn)義的解決方法

    今天小編就為大家分享一篇golang json.Marshal 特殊html字符被轉(zhuǎn)義的解決方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧 ...

    李浩的life12792020-05-27
  • Golanggo日志系統(tǒng)logrus顯示文件和行號的操作

    go日志系統(tǒng)logrus顯示文件和行號的操作

    這篇文章主要介紹了go日志系統(tǒng)logrus顯示文件和行號的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    SmallQinYan12302021-02-02
  • GolangGolang通脈之?dāng)?shù)據(jù)類型詳情

    Golang通脈之?dāng)?shù)據(jù)類型詳情

    這篇文章主要介紹了Golang通脈之?dāng)?shù)據(jù)類型,在編程語言中標(biāo)識符就是定義的具有某種意義的詞,比如變量名、常量名、函數(shù)名等等,Go語言中標(biāo)識符允許由...

    4272021-11-24
  • Golanggolang的httpserver優(yōu)雅重啟方法詳解

    golang的httpserver優(yōu)雅重啟方法詳解

    這篇文章主要給大家介紹了關(guān)于golang的httpserver優(yōu)雅重啟的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,...

    helight2992020-05-14
  • Golanggolang 通過ssh代理連接mysql的操作

    golang 通過ssh代理連接mysql的操作

    這篇文章主要介紹了golang 通過ssh代理連接mysql的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    a165861639710342021-03-08
  • Golanggo語言制作端口掃描器

    go語言制作端口掃描器

    本文給大家分享的是使用go語言編寫的TCP端口掃描器,可以選擇IP范圍,掃描的端口,以及多線程,有需要的小伙伴可以參考下。 ...

    腳本之家3642020-04-25
  • Golanggolang如何使用struct的tag屬性的詳細(xì)介紹

    golang如何使用struct的tag屬性的詳細(xì)介紹

    這篇文章主要介紹了golang如何使用struct的tag屬性的詳細(xì)介紹,從例子說起,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看...

    Go語言中文網(wǎng)11352020-05-21
主站蜘蛛池模板: 久久噜国产精品拍拍拍拍 | 国产精品va在线观看不 | 亚洲女人国产香蕉久久精品 | 婷婷色综合网 | 拔插拔插8x8x海外华人免费视频 | 三级午夜宅宅伦不卡在线 | 三级黄片毛片 | 亚洲AV无码A片在线观看蜜桃 | 成人中文字幕在线观看 | 美女靠逼免费视频 | 免费网站看v片在线香蕉 | 女女性恋爱视频入口 | 99视频有精品视频免费观看 | 丁香五香天堂 | 男神插曲女生动漫完整版动漫 | 草莓秋葵菠萝蜜绿巨人污 | 亚洲欧美精品一区天堂久久 | 91色在线观看国产 | 北海市副市长黄江老公 | 无码射肉在线播放视频 | 成人精品mv视频在线观看 | 久久亚洲网站 | 色多多视频在线 | 欧美在线一级视频 | 午夜A级理论片左线播放 | 亚洲国产99在线精品一区69堂 | 国产日韩成人 | 日韩在线 在线播放 | 啊好痛嗯轻一点免费 | 午夜精品免费 | 国产午夜一区二区在线观看 | 日本卡一卡2卡3卡4精品卡无人区 | 精品91自产拍在线观看99re | 亚洲品质自拍网站 | 欧美一区二区三区四区视频 | 91色在线观看国产 | 欧美激情影音先锋 | 明星裸乳照无奶罩 | 无人区在线观看免费观看 | 日本96在线精品视频免费观看 | 午夜国产精品视频 |