最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go系列教程之反射的用法

 更新時(shí)間:2019年01月18日 11:02:22   作者:Noluye  
這篇文章主要介紹了Go系列教程之反射的用法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

反射是 Go 語(yǔ)言的高級(jí)主題之一。我會(huì)盡可能讓它變得簡(jiǎn)單易懂。

本教程分為如下小節(jié)。

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

讓我們來(lái)逐個(gè)討論這些章節(jié)。

什么是反射?

反射就是程序能夠在運(yùn)行時(shí)檢查變量和值,求出它們的類型。你可能還不太懂,這沒(méi)關(guān)系。在本教程結(jié)束后,你就會(huì)清楚地理解反射,所以跟著我們的教程學(xué)習(xí)吧。

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

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

我來(lái)解釋一下吧。下面我們編寫一個(gè)簡(jiǎn)單的程序。

package main

import (
 "fmt"
)

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

在 playground 上運(yùn)行

在上面的程序中,i 的類型在編譯時(shí)就知道了,然后我們?cè)谙乱恍写蛴〕?i。這里沒(méi)什么特別之處。

現(xiàn)在了解一下,需要在運(yùn)行時(shí)求得變量類型的情況。假如我們要編寫一個(gè)簡(jiǎn)單的函數(shù),它接收結(jié)構(gòu)體作為參數(shù),并用它來(lái)創(chuàng)建一個(gè) SQL 插入查詢。

考慮下面的程序:

package main

import (
 "fmt"
)

type order struct {
 ordId  int
 customerId int
}

func main() {
 o := order{
  ordId:  1234,
  customerId: 567,
 }
 fmt.Println(o)
}

在 playground 上運(yùn)行

在上面的程序中,我們需要編寫一個(gè)函數(shù),接收結(jié)構(gòu)體變量 o 作為參數(shù),返回下面的 SQL 插入查詢。

insert into order values(1234, 567)

這個(gè)函數(shù)寫起來(lái)很簡(jiǎn)單。我們現(xiàn)在編寫這個(gè)函數(shù)。

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 上運(yùn)行

在第 12 行,createQuery 函數(shù)用 o 的兩個(gè)字段(ordId 和 customerId),創(chuàng)建了插入查詢。該程序會(huì)輸出:

insert into order values(1234, 567)

現(xiàn)在我們來(lái)升級(jí)這個(gè)查詢生成器。如果我們想讓它變得通用,可以適用于任何結(jié)構(gòu)體類型,該怎么辦呢?我們用程序來(lái)理解一下。

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() {

}

我們的目標(biāo)就是完成 createQuery 函數(shù)(上述程序中的第 16 行),它可以接收任何結(jié)構(gòu)體作為參數(shù),根據(jù)結(jié)構(gòu)體的字段創(chuàng)建插入查詢。

例如,如果我們傳入下面的結(jié)構(gòu)體:

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

createQuery 函數(shù)應(yīng)該返回:

insert into order values (1234, 567)

類似地,如果我們傳入:

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

該函數(shù)會(huì)返回:

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

由于 createQuery 函數(shù)應(yīng)該適用于任何結(jié)構(gòu)體,因此它接收 interface{} 作為參數(shù)。為了簡(jiǎn)單起見(jiàn),我們只處理包含 string 和 int 類型字段的結(jié)構(gòu)體,但可以擴(kuò)展為包含任何類型的字段。

createQuery 函數(shù)應(yīng)該適用于所有的結(jié)構(gòu)體。因此,要編寫這個(gè)函數(shù),就必須在運(yùn)行時(shí)檢查傳遞過(guò)來(lái)的結(jié)構(gòu)體參數(shù)的類型,找到結(jié)構(gòu)體字段,接著創(chuàng)建查詢。這時(shí)就需要用到反射了。在本教程的下一步,我們將會(huì)學(xué)習(xí)如何使用 reflect 包來(lái)實(shí)現(xiàn)它。

reflect 包

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

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

reflect.Type 和 reflect.Value

reflect.Type 表示 interface{} 的具體類型,而 reflect.Value 表示它的具體值。reflect.TypeOf() 和 reflect.ValueOf() 兩個(gè)函數(shù)可以分別返回 reflect.Type 和 reflect.Value。這兩種類型是我們創(chuàng)建查詢生成器的基礎(chǔ)。我們現(xiàn)在用一個(gè)簡(jiǎn)單的例子來(lái)理解這兩種類型。

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 上運(yùn)行

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

上述程序會(huì)打印:

Type  main.order
Value  {456 56}

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

relfect.Kind

reflect 包中還有一個(gè)重要的類型:Kind。

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

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 上運(yùn)行

上述程序會(huì)輸出:

Type  main.order
Kind  struct

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

NumField() 和 Field() 方法

NumField() 方法返回結(jié)構(gòu)體中字段的數(shù)量,而 Field(i int) 方法返回字段 i 的 reflect.Value。

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 上運(yùn)行

在上面的程序中,因?yàn)?NumField 方法只能在結(jié)構(gòu)體上使用,我們?cè)诘?14 行首先檢查了 q 的類別是 struct。程序的其他代碼很容易看懂,不作解釋。該程序會(huì)輸出:

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 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 上運(yùn)行

在上面程序中的第 10 行,我們?nèi)〕?reflect.Value,并轉(zhuǎn)換為 int64,而在第 13 行,我們?nèi)〕?reflect.Value 并將其轉(zhuǎn)換為 string。該程序會(huì)輸出:

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

完整的程序

現(xiàn)在我們已經(jīng)具備足夠多的知識(shí),來(lái)完成我們的查詢生成器了,我們來(lái)實(shí)現(xiàn)它把。

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 上運(yùn)行

在第 22 行,我們首先檢查了傳來(lái)的參數(shù)是否是一個(gè)結(jié)構(gòu)體。在第 23 行,我們使用了 Name() 方法,從該結(jié)構(gòu)體的 reflect.Type 獲取了結(jié)構(gòu)體的名字。接下來(lái)一行,我們用 t 來(lái)創(chuàng)建查詢。

在第 28 行,case 語(yǔ)句 檢查了當(dāng)前字段是否為 reflect.Int,如果是的話,我們會(huì)取到該字段的值,并使用 Int() 方法轉(zhuǎn)換為 int64。if else 語(yǔ)句用于處理邊界情況。請(qǐng)?zhí)砑尤罩緛?lái)理解為什么需要它。在第 34 行,我們用來(lái)相同的邏輯來(lái)取到 string。

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

該程序會(huì)輸出:

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

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

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

我們應(yīng)該使用反射嗎?

我們已經(jīng)展示了反射的實(shí)際應(yīng)用,現(xiàn)在考慮一個(gè)很現(xiàn)實(shí)的問(wèn)題。我們應(yīng)該使用反射嗎?我想引用 Rob Pike 關(guān)于使用反射的格言,來(lái)回答這個(gè)問(wèn)題。

清晰優(yōu)于聰明。而反射并不是一目了然的。

反射是 Go 語(yǔ)言中非常強(qiáng)大和高級(jí)的概念,我們應(yīng)該小心謹(jǐn)慎地使用它。使用反射編寫清晰和可維護(hù)的代碼是十分困難的。你應(yīng)該盡可能避免使用它,只在必須用到它時(shí),才使用反射。

本教程到此結(jié)束。希望你們喜歡。祝你愉快。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • gorm FirstOrCreate和受影響的行數(shù)實(shí)例

    gorm FirstOrCreate和受影響的行數(shù)實(shí)例

    這篇文章主要介紹了gorm FirstOrCreate和受影響的行數(shù)實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12
  • 詳解Go如何優(yōu)雅的對(duì)時(shí)間進(jìn)行格式化

    詳解Go如何優(yōu)雅的對(duì)時(shí)間進(jìn)行格式化

    這篇文章主要為大家詳細(xì)介紹了Go語(yǔ)言中是如何優(yōu)雅的對(duì)時(shí)間進(jìn)行格式化的,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下
    2023-06-06
  • 如何使用Go檢測(cè)用戶本地是否安裝chrome

    如何使用Go檢測(cè)用戶本地是否安裝chrome

    這篇文章主要為大家詳細(xì)介紹了如何使用Go檢測(cè)用戶本地是否安裝chrome,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-10-10
  • Golang WaitGroup實(shí)現(xiàn)原理解析

    Golang WaitGroup實(shí)現(xiàn)原理解析

    WaitGroup是Golang并發(fā)的兩種方式之一,一個(gè)是Channel,另一個(gè)是WaitGroup,下面這篇文章主要給大家介紹了關(guān)于golang基礎(chǔ)之waitgroup用法以及使用要點(diǎn)的相關(guān)資料,需要的朋友可以參考下
    2023-02-02
  • Go語(yǔ)言實(shí)現(xiàn)百萬(wàn)級(jí)WebSocket連接架構(gòu)設(shè)計(jì)及服務(wù)優(yōu)化

    Go語(yǔ)言實(shí)現(xiàn)百萬(wàn)級(jí)WebSocket連接架構(gòu)設(shè)計(jì)及服務(wù)優(yōu)化

    本文將詳細(xì)介紹如何在Go中構(gòu)建一個(gè)能夠支持百萬(wàn)級(jí)WebSocket連接的服務(wù),包括系統(tǒng)架構(gòu)設(shè)計(jì)、性能優(yōu)化策略以及具體的實(shí)現(xiàn)步驟和代碼示例
    2024-01-01
  • Go語(yǔ)言中Goroutine的設(shè)置方式

    Go語(yǔ)言中Goroutine的設(shè)置方式

    這篇文章介紹了Go語(yǔ)言中Goroutine的設(shè)置方式,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-07-07
  • 深入解析Go語(yǔ)言中HTTP請(qǐng)求處理的底層實(shí)現(xiàn)

    深入解析Go語(yǔ)言中HTTP請(qǐng)求處理的底層實(shí)現(xiàn)

    本文將詳細(xì)介紹?Go?語(yǔ)言中?HTTP?請(qǐng)求處理的底層機(jī)制,包括工作流程、創(chuàng)建?Listen?Socket?監(jiān)聽(tīng)端口、接收客戶端請(qǐng)求并建立連接以及處理客戶端請(qǐng)求并返回響應(yīng)等,需要的朋友可以參考下
    2023-05-05
  • 淺析Go中序列化與反序列化的基本使用

    淺析Go中序列化與反序列化的基本使用

    序列化是指將對(duì)象轉(zhuǎn)換成字節(jié)流,從而存儲(chǔ)對(duì)象或?qū)?duì)象傳輸?shù)絻?nèi)存、數(shù)據(jù)庫(kù)或文件的過(guò)程,反向過(guò)程稱為“反序列化”。本文主要介紹了Go中序列化與反序列化的基本使用,需要的可以參考一下
    2023-04-04
  • golang并發(fā)編程使用Select語(yǔ)句的實(shí)現(xiàn)

    golang并發(fā)編程使用Select語(yǔ)句的實(shí)現(xiàn)

    Go語(yǔ)言中的select語(yǔ)句是并發(fā)編程中的重要工具,允許Goroutine等待多個(gè)通道操作,它阻塞直至任一case可執(zhí)行,可用于接收數(shù)據(jù)、實(shí)現(xiàn)超時(shí)機(jī)制和非阻塞通道操作,感興趣的可以了解一下
    2024-10-10
  • Golang Http請(qǐng)求返回結(jié)果處理

    Golang Http請(qǐng)求返回結(jié)果處理

    本文主要介紹了Golang Http請(qǐng)求返回結(jié)果處理,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08

最新評(píng)論

南岸区| 凤凰县| 自贡市| 山西省| 蒙城县| 临安市| 大石桥市| 揭阳市| 达孜县| 东兰县| 印江| 夏河县| 上林县| 岱山县| 元朗区| 南康市| 图木舒克市| 内丘县| 海南省| 天祝| 贵港市| 榆树市| 沅江市| 济阳县| 合山市| 恩施市| 娄烦县| 应城市| 沙坪坝区| 孟连| 清流县| 许昌县| 灵丘县| 塘沽区| 涞源县| 灵丘县| 扎鲁特旗| 北票市| 交城县| 卢龙县| 滦南县|