亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Golang > 正文

Go系列教程之反射的用法

2020-04-01 18:50:09
字體:
來源:轉載
供稿:網友

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

本教程分為如下小節。

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

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

什么是反射?

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

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

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

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

package mainimport ( "fmt")func main() { i := 10 fmt.Printf("%d %T", i, i)}

在 playground 上運行

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

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

考慮下面的程序:

package mainimport ( "fmt")type order struct { ordId  int customerId int}func main() { o := order{  ordId:  1234,  customerId: 567, } fmt.Println(o)}

在 playground 上運行

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

insert into order values(1234, 567)

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

package mainimport ( "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),創建了插入查詢。該程序會輸出:

insert into order values(1234, 567)

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

package maintype 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 行),它可以接收任何結構體作為參數,根據結構體的字段創建插入查詢。

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

o := order { ordId: 1234, customerId: 567}

createQuery 函數應該返回:

insert into order values (1234, 567)

類似地,如果我們傳入:

 e := employee {  name: "Naveen",  id: 565,  address: "Science Park Road, Singapore",  salary: 90000,  country: "Singapore", }

該函數會返回:

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。這兩種類型是我們創建查詢生成器的基礎。我們現在用一個簡單的例子來理解這兩種類型。

package mainimport ( "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{} 的具體值。

上述程序會打?。?/p>

Type  main.order
Value  {456 56}

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

relfect.Kind

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

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

package mainimport ( "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。

package mainimport ( "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。

package mainimport ( "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

完整的程序

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

package mainimport ( "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 函數傳入不支持的類型時,程序發生崩潰。程序的其他代碼是自解釋性的。我建議你在合適的地方添加日志,檢查輸出,來更好地理解這個程序。

該程序會輸出:

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

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

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

我們應該使用反射嗎?

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

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

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

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


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品免费视频久久久| 国产一区二区三区日韩欧美| 97香蕉久久夜色精品国产| 国产一区在线播放| 日韩一级裸体免费视频| 成人免费视频在线观看超级碰| 国产精品极品尤物在线观看| 国产婷婷色综合av蜜臀av| 国产97在线视频| 欧美视频免费在线| 久久久999精品视频| 欧美性猛交xxxx免费看| 亚洲色图五月天| 国产精品久久久久久影视| 欧美丰满少妇xxxxx做受| 国产亚洲精品美女| 亚洲风情亚aⅴ在线发布| 亚洲图片欧洲图片av| 久久av在线看| 国产精品ⅴa在线观看h| 亚洲天堂第一页| 深夜成人在线观看| 亚洲视频视频在线| 久久久女人电视剧免费播放下载| 亚洲精品有码在线| 国产精品三级在线| 91av在线播放| 夜夜躁日日躁狠狠久久88av| 在线观看国产精品淫| 久久69精品久久久久久久电影好| 羞羞色国产精品| 欧美激情videos| 日韩av在线最新| 欧美疯狂性受xxxxx另类| 91亚洲国产精品| 亚洲欧美日本伦理| 亚洲一区二区久久久久久| 国产亚洲视频在线| 久久国产精品亚洲| 538国产精品一区二区在线| 欧美成人亚洲成人| 亚洲最大激情中文字幕| 日韩**中文字幕毛片| 久久久97精品| 91wwwcom在线观看| 欧美性生交xxxxx久久久| 91精品在线一区| 久久伊人91精品综合网站| 97欧美精品一区二区三区| 国产视频在线观看一区二区| 国产精品一区二区久久久久| 黑人巨大精品欧美一区二区一视频| 91麻豆国产精品| 欧美视频在线观看 亚洲欧| 欧美激情videos| 亚洲www在线观看| 欧美理论在线观看| 91免费看视频.| 欧美激情喷水视频| 亚洲自拍欧美另类| 欧美乱人伦中文字幕在线| 久久亚洲国产成人| 伊人av综合网| 性欧美长视频免费观看不卡| 97精品视频在线| 九九热在线精品视频| 久久久久久国产精品三级玉女聊斋| 国产一区二区三区欧美| 中文字幕国产精品| 国产一区二区日韩| 中文国产亚洲喷潮| 国产人妖伪娘一区91| 欧美激情va永久在线播放| 国产欧美一区二区三区在线看| 在线精品91av| 少妇久久久久久| 欧美另类极品videosbest最新版本| 九九热r在线视频精品| 亚洲一区av在线播放| 精品福利在线视频| 日韩一区二区在线视频| 亚洲国产成人精品一区二区| 精品视频在线播放| 成人午夜在线视频一区| 欧美成人免费网| 九九热最新视频//这里只有精品| 国产亚洲精品久久久久动| 亚洲视频在线播放| 大桥未久av一区二区三区| 日韩精品黄色网| 欧美日韩电影在线观看| 国产精品影院在线观看| 日韩中文有码在线视频| 久久躁日日躁aaaaxxxx| 亚洲高清久久网| 日韩av在线天堂网| 91久久精品国产91久久性色| 欧美另类在线播放| 欧美亚洲第一区| 欧美一区二区影院| 亚洲最大在线视频| 成人高清视频观看www| 精品视频—区二区三区免费| 日韩欧美成人区| 久久亚洲精品成人| 日韩欧美中文字幕在线观看| 国产精品视频最多的网站| 日韩欧美在线播放| 日韩精品在线观| 亚洲日本欧美中文幕| 欧美成人性色生活仑片| 欧美一级视频一区二区| 欧美中文字幕视频| 日韩中文字幕在线精品| 尤物tv国产一区| 日韩欧美国产激情| 国产精品青青在线观看爽香蕉| 日韩欧美国产一区二区| 在线a欧美视频| 98视频在线噜噜噜国产| 日韩激情第一页| 国产精品影院在线观看| 日韩电视剧免费观看网站| 欧美床上激情在线观看| 欧美一区二区大胆人体摄影专业网站| 色无极影院亚洲| 欧美午夜激情视频| 98视频在线噜噜噜国产| 欧美高清在线视频观看不卡| 亚洲欧洲国产一区| 欧美xxxx做受欧美.88| 国产精品自产拍在线观| 国产成人精品一区二区| 韩国日本不卡在线| 亚洲最新中文字幕| 国产精品久久久久久久美男| 久久精品亚洲94久久精品| 大量国产精品视频| 欧美精品videosex极品1| 久久视频在线观看免费| 亚洲女性裸体视频| 日本高清不卡的在线| 国产视频一区在线| 欧美韩国理论所午夜片917电影| 欧洲精品久久久| 在线播放国产一区中文字幕剧情欧美| 欧美激情一区二区三区久久久| 97香蕉久久夜色精品国产| 国产成人精品免费视频| 国产精品久久久久av| 播播国产欧美激情| 久久福利网址导航| 亚洲欧美日韩国产成人| 欧美一区二区.| 午夜精品久久久久久久男人的天堂| 久久久成人精品| 中文字幕日韩在线视频| 久久精品国产电影| 97在线观看视频| 日本国产高清不卡| 91视频免费在线| 国产成人黄色av| 日韩免费视频在线观看| 日本老师69xxx|