国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

腳本之家,腳本語言編程技術及教程分享平臺!
分類導航

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

服務器之家 - 腳本之家 - Golang - Go系列教程之反射的用法

Go系列教程之反射的用法

2020-05-22 09:42Noluye Golang

這篇文章主要介紹了Go系列教程之反射的用法,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

反射是 Go 語言的高級主題之一。我會盡可能讓它變得簡單易懂。

本教程分為如下小節。

  • 什么是反射?
  • 為何需要檢查變量,確定變量的類型?
  • reflect 包
    • reflect.Type 和 reflect.Value
    • reflect.Kind
    • NumField() 和 Field() 方法
    • Int() 和 String() 方法
  • 完整的程序
  • 我們應該使用反射嗎?

讓我們來逐個討論這些章節。

什么是反射?

反射就是程序能夠在運行時檢查變量和值,求出它們的類型。你可能還不太懂,這沒關系。在本教程結束后,你就會清楚地理解反射,所以跟著我們的教程學習吧。

為何需要檢查變量,確定變量的類型?

在學習反射時,所有人首先面臨的疑惑就是:如果程序中每個變量都是我們自己定義的,那么在編譯時就可以知道變量類型了,為什么我們還需要在運行時檢查變量,求出它的類型呢?沒錯,在大多數時候都是這樣,但并非總是如此。

我來解釋一下吧。下面我們編寫一個簡單的程序。

?
1
2
3
4
5
6
7
8
9
10
package main
 
import (
 "fmt"
)
 
func main() {
 i := 10
 fmt.Printf("%d %T", i, i)
}

在 playground 上運行

在上面的程序中,i 的類型在編譯時就知道了,然后我們在下一行打印出 i。這里沒什么特別之處。

現在了解一下,需要在運行時求得變量類型的情況。假如我們要編寫一個簡單的函數,它接收結構體作為參數,并用它來創建一個 SQL 插入查詢。

考慮下面的程序:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main
 
import (
 "fmt"
)
 
type order struct {
 ordId  int
 customerId int
}
 
func main() {
 o := order{
  ordId:  1234,
  customerId: 567,
 }
 fmt.Println(o)
}

在 playground 上運行

在上面的程序中,我們需要編寫一個函數,接收結構體變量 o 作為參數,返回下面的 SQL 插入查詢。

?
1
insert into order values(1234, 567)

這個函數寫起來很簡單。我們現在編寫這個函數。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main
 
import (
 "fmt"
)
 
type order struct {
 ordId  int
 customerId int
}
 
func createQuery(o order) string {
 i := fmt.Sprintf("insert into order values(%d, %d)", o.ordId, o.customerId)
 return i
}
 
func main() {
 o := order{
  ordId:  1234,
  customerId: 567,
 }
 fmt.Println(createQuery(o))
}

在 playground 上運行

在第 12 行,createQuery 函數用 o 的兩個字段(ordId 和 customerId),創建了插入查詢。該程序會輸出:

?
1
insert into order values(1234, 567)

現在我們來升級這個查詢生成器。如果我們想讓它變得通用,可以適用于任何結構體類型,該怎么辦呢?我們用程序來理解一下。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main
 
type order struct {
 ordId  int
 customerId int
}
 
type employee struct {
 name string
 id int
 address string
 salary int
 country string
}
 
func createQuery(q interface{}) string {
}
 
func main() {
 
}

我們的目標就是完成 createQuery 函數(上述程序中的第 16 行),它可以接收任何結構體作為參數,根據結構體的字段創建插入查詢。

例如,如果我們傳入下面的結構體:

?
1
2
3
4
o := order {
 ordId: 1234,
 customerId: 567
}

createQuery 函數應該返回:

?
1
insert into order values (1234, 567)

類似地,如果我們傳入:

?
1
2
3
4
5
6
7
e := employee {
 name: "Naveen",
 id: 565,
 address: "Science Park Road, Singapore",
 salary: 90000,
 country: "Singapore",
}

該函數會返回:

?
1
insert into employee values("Naveen", 565, "Science Park Road, Singapore", 90000, "Singapore")

由于 createQuery 函數應該適用于任何結構體,因此它接收 interface{} 作為參數。為了簡單起見,我們只處理包含 string 和 int 類型字段的結構體,但可以擴展為包含任何類型的字段。

createQuery 函數應該適用于所有的結構體。因此,要編寫這個函數,就必須在運行時檢查傳遞過來的結構體參數的類型,找到結構體字段,接著創建查詢。這時就需要用到反射了。在本教程的下一步,我們將會學習如何使用 reflect 包來實現它。

reflect 包

在 Go 語言中,reflect 實現了運行時反射。reflect 包會幫助識別 interface{} 變量的底層具體類型和具體值。這正是我們所需要的。createQuery 函數接收 interface{} 參數,根據它的具體類型和具體值,創建 SQL 查詢。這正是 reflect 包能夠幫助我們的地方。

在編寫我們通用的查詢生成器之前,我們首先需要了解 reflect 包中的幾種類型和方法。讓我們來逐個了解。

reflect.Type 和 reflect.Value

reflect.Type 表示 interface{} 的具體類型,而 reflect.Value 表示它的具體值。reflect.TypeOf() 和 reflect.ValueOf() 兩個函數可以分別返回 reflect.Type 和 reflect.Value。這兩種類型是我們創建查詢生成器的基礎。我們現在用一個簡單的例子來理解這兩種類型。

?
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
package main
 
import (
 "fmt"
 "reflect"
)
 
type order struct {
 ordId  int
 customerId int
}
 
func createQuery(q interface{}) {
 t := reflect.TypeOf(q)
 v := reflect.ValueOf(q)
 fmt.Println("Type ", t)
 fmt.Println("Value ", v)
 
 
}
func main() {
 o := order{
  ordId:  456,
  customerId: 56,
 }
 createQuery(o)
 
}

在 playground 上運行

在上面的程序中,第 13 行的 createQuery 函數接收 interface{} 作為參數。在第 14 行,reflect.TypeOf 接收了參數 interface{},返回了reflect.Type,它包含了傳入的 interface{} 參數的具體類型。同樣地,在第 15 行,reflect.ValueOf 函數接收參數 interface{},并返回了 reflect.Value,它包含了傳來的 interface{} 的具體值。

上述程序會打印:

Type  main.order
Value  {456 56}

從輸出我們可以看到,程序打印了接口的具體類型和具體值。

relfect.Kind

reflect 包中還有一個重要的類型:Kind。

在反射包中,Kind 和 Type 的類型可能看起來很相似,但在下面程序中,可以很清楚地看出它們的不同之處。

?
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
package main
 
import (
 "fmt"
 "reflect"
)
 
type order struct {
 ordId  int
 customerId int
}
 
func createQuery(q interface{}) {
 t := reflect.TypeOf(q)
 k := t.Kind()
 fmt.Println("Type ", t)
 fmt.Println("Kind ", k)
 
 
}
func main() {
 o := order{
  ordId:  456,
  customerId: 56,
 }
 createQuery(o)
 
}

在 playground 上運行

上述程序會輸出:

Type  main.order
Kind  struct

我想你應該很清楚兩者的區別了。Type 表示 interface{} 的實際類型(在這里是 main.Order),而 Kind 表示該類型的特定類別(在這里是 struct)。

NumField() 和 Field() 方法

NumField() 方法返回結構體中字段的數量,而 Field(i int) 方法返回字段 i 的 reflect.Value。

?
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
package main
 
import (
 "fmt"
 "reflect"
)
 
type order struct {
 ordId  int
 customerId int
}
 
func createQuery(q interface{}) {
 if reflect.ValueOf(q).Kind() == reflect.Struct {
  v := reflect.ValueOf(q)
  fmt.Println("Number of fields", v.NumField())
  for i := 0; i < v.NumField(); i++ {
   fmt.Printf("Field:%d type:%T value:%v\n", i, v.Field(i), v.Field(i))
  }
 }
 
}
func main() {
 o := order{
  ordId:  456,
  customerId: 56,
 }
 createQuery(o)
}

在 playground 上運行

在上面的程序中,因為 NumField 方法只能在結構體上使用,我們在第 14 行首先檢查了 q 的類別是 struct。程序的其他代碼很容易看懂,不作解釋。該程序會輸出:

Number of fields 2
Field:0 type:reflect.Value value:456
Field:1 type:reflect.Value value:56

Int() 和 String() 方法

Int 和 String 可以幫助我們分別取出 reflect.Value 作為 int64 和 string。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main
 
import (
 "fmt"
 "reflect"
)
 
func main() {
 a := 56
 x := reflect.ValueOf(a).Int()
 fmt.Printf("type:%T value:%v\n", x, x)
 b := "Naveen"
 y := reflect.ValueOf(b).String()
 fmt.Printf("type:%T value:%v\n", y, y)
 
}

在 playground 上運行

在上面程序中的第 10 行,我們取出 reflect.Value,并轉換為 int64,而在第 13 行,我們取出 reflect.Value 并將其轉換為 string。該程序會輸出:

type:int64 value:56
type:string value:Naveen

完整的程序

現在我們已經具備足夠多的知識,來完成我們的查詢生成器了,我們來實現它把。

?
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package main
 
import (
 "fmt"
 "reflect"
)
 
type order struct {
 ordId  int
 customerId int
}
 
type employee struct {
 name string
 id  int
 address string
 salary int
 country string
}
 
func createQuery(q interface{}) {
 if reflect.ValueOf(q).Kind() == reflect.Struct {
  t := reflect.TypeOf(q).Name()
  query := fmt.Sprintf("insert into %s values(", t)
  v := reflect.ValueOf(q)
  for i := 0; i < v.NumField(); i++ {
   switch v.Field(i).Kind() {
   case reflect.Int:
    if i == 0 {
     query = fmt.Sprintf("%s%d", query, v.Field(i).Int())
    } else {
     query = fmt.Sprintf("%s, %d", query, v.Field(i).Int())
    }
   case reflect.String:
    if i == 0 {
     query = fmt.Sprintf("%s\"%s\"", query, v.Field(i).String())
    } else {
     query = fmt.Sprintf("%s, \"%s\"", query, v.Field(i).String())
    }
   default:
    fmt.Println("Unsupported type")
    return
   }
  }
  query = fmt.Sprintf("%s)", query)
  fmt.Println(query)
  return
 
 }
 fmt.Println("unsupported type")
}
 
func main() {
 o := order{
  ordId:  456,
  customerId: 56,
 }
 createQuery(o)
 
 e := employee{
  name: "Naveen",
  id:  565,
  address: "Coimbatore",
  salary: 90000,
  country: "India",
 }
 createQuery(e)
 i := 90
 createQuery(i)
 
}

在 playground 上運行

在第 22 行,我們首先檢查了傳來的參數是否是一個結構體。在第 23 行,我們使用了 Name() 方法,從該結構體的 reflect.Type 獲取了結構體的名字。接下來一行,我們用 t 來創建查詢。

在第 28 行,case 語句 檢查了當前字段是否為 reflect.Int,如果是的話,我們會取到該字段的值,并使用 Int() 方法轉換為 int64。if else 語句用于處理邊界情況。請添加日志來理解為什么需要它。在第 34 行,我們用來相同的邏輯來取到 string。

我們還作了額外的檢查,以防止 createQuery 函數傳入不支持的類型時,程序發生崩潰。程序的其他代碼是自解釋性的。我建議你在合適的地方添加日志,檢查輸出,來更好地理解這個程序。

該程序會輸出:

?
1
2
3
insert into order values(456, 56)
insert into employee values("Naveen", 565, "Coimbatore", 90000, "India")
unsupported type

至于向輸出的查詢中添加字段名,我們把它留給讀者作為練習。請嘗試著修改程序,打印出以下格式的查詢。

?
1
insert into order(ordId, customerId) values(456, 56)

我們應該使用反射嗎?

我們已經展示了反射的實際應用,現在考慮一個很現實的問題。我們應該使用反射嗎?我想引用 Rob Pike 關于使用反射的格言,來回答這個問題。

清晰優于聰明。而反射并不是一目了然的。

反射是 Go 語言中非常強大和高級的概念,我們應該小心謹慎地使用它。使用反射編寫清晰和可維護的代碼是十分困難的。你應該盡可能避免使用它,只在必須用到它時,才使用反射。

本教程到此結束。希望你們喜歡。祝你愉快。希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:https://studygolang.com/articles/13178

延伸 · 閱讀

精彩推薦
  • Golanggo語言制作端口掃描器

    go語言制作端口掃描器

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

    腳本之家3642020-04-25
  • Golanggo日志系統logrus顯示文件和行號的操作

    go日志系統logrus顯示文件和行號的操作

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

    SmallQinYan12302021-02-02
  • Golanggolang如何使用struct的tag屬性的詳細介紹

    golang如何使用struct的tag屬性的詳細介紹

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

    Go語言中文網11352020-05-21
  • GolangGolang中Bit數組的實現方式

    Golang中Bit數組的實現方式

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

    天易獨尊11682021-06-09
  • Golanggolang的httpserver優雅重啟方法詳解

    golang的httpserver優雅重啟方法詳解

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

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

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

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

    a165861639710342021-03-08
  • GolangGolang通脈之數據類型詳情

    Golang通脈之數據類型詳情

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

    4272021-11-24
  • Golanggolang json.Marshal 特殊html字符被轉義的解決方法

    golang json.Marshal 特殊html字符被轉義的解決方法

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

    李浩的life12792020-05-27
主站蜘蛛池模板: 小情侣高清国产在线播放 | 日本福利视频网 | 在线中文视频 | av久草 | 中文字幕在线一区二区三区 | 91视频免费 | 精品视频免费 | 午夜在线小视频 | 亚洲精品乱码久久久久久蜜糖图片 | 1区2区视频 | 亚洲精品视频在线播放 | 亚洲精品一区二区在线 | 国产98色在线 | 日韩 | 国产精品视频一区二区三区不卡 | 国产玖玖 | 高清视频一区 | 精品无码三级在线观看视频 | 国产精品久久天天躁 | 亚洲精品久久久久久久久久久久久 | 亚洲精品综合 | 日韩视频专区 | 午夜精品久久久久 | 国产在线精品一区二区三区 | 偷拍一区二区三区 | 亚洲伊人久久综合 | av毛片免费看 | 成人网av| 日韩亚洲| 国产精品资源在线观看 | 欧美成年黄网站色视频 | 精品国产综合 | 特黄色一级片 | 久久久久黄 | 黄免费 | 国产视频网 | 国产一区二区三区免费播放 | 欧美精品一区在线 | 午夜在线小视频 | 伊人色综合网 | 日韩中文字幕 | 亚洲无吗电影 |