欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go系列教程之反射的用法

 更新時間:2019年01月18日 11:02:22   作者:Noluye  
這篇文章主要介紹了Go系列教程之反射的用法,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

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

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

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

讓我們來逐個討論這些章節(jié)。

什么是反射?

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

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

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

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

package main

import (
 "fmt"
)

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

在 playground 上運行

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

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

考慮下面的程序:

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 插入查詢。

insert into order values(1234, 567)

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

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

insert into order values(1234, 567)

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

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

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

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

reflect 包

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

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

reflect.Type 和 reflect.Value

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

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{} 的具體值。

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

Type  main.order
Value  {456 56}

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

relfect.Kind

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

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

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

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

NumField() 和 Field() 方法

NumField() 方法返回結構體中字段的數量,而 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 上運行

在上面的程序中,因為 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 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

完整的程序

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

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 來創(chuàng)建查詢。

在第 28 行,case 語句 檢查了當前字段是否為 reflect.Int,如果是的話,我們會取到該字段的值,并使用 Int() 方法轉換為 int64。if else 語句用于處理邊界情況。請?zhí)砑尤罩緛砝斫鉃槭裁葱枰?。在?34 行,我們用來相同的邏輯來取到 string。

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

該程序會輸出:

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 關于使用反射的格言,來回答這個問題。

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

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

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

相關文章

  • Golang中的archive/zip包的常用函數詳解

    Golang中的archive/zip包的常用函數詳解

    Golang 中的 archive/zip 包用于處理 ZIP 格式的壓縮文件,提供了一系列用于創(chuàng)建、讀取和解壓縮 ZIP 格式文件的函數和類型,下面小編就來和大家講解下常用函數吧
    2023-08-08
  • Go語言實現簡單留言板的方法

    Go語言實現簡單留言板的方法

    這篇文章主要介紹了Go語言實現簡單留言板的方法,涉及數據庫、模板頁面元素等留言板相關技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-02-02
  • go寫文件后出現大量NUL字符問題解決

    go寫文件后出現大量NUL字符問題解決

    本文主要介紹了go寫文件后出現大量NUL字符問題解決,由于每次寫的時候設置的長度都是64,在某次不足64時,byte切片空余位置被填充為空字符,下面就來介紹一下如何解決
    2023-12-12
  • 一文帶你探索Go語言中crypto/md5標準庫的強大功能

    一文帶你探索Go語言中crypto/md5標準庫的強大功能

    我們將從MD5算法的基礎知識入手,逐步深入到如何在Go中有效使用crypto/md5標準庫,包括基本的使用方法、實際應用案例分析,以及性能和安全性的考量,需要的可以參考下
    2024-02-02
  • golang與pgsql交互的實現

    golang與pgsql交互的實現

    本文主要介紹了golang與pgsql交互的實現,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-03-03
  • Go語言循環(huán)遍歷含有中文的字符串的方法小結

    Go語言循環(huán)遍歷含有中文的字符串的方法小結

    這篇文章主要介紹了Go語言循環(huán)遍歷含有中文的字符串的幾種方法,文章通過代碼示例講解的非常詳細,具有一定的參考價值,感興趣的小伙伴跟著小編一起來看看吧
    2023-07-07
  • Go?CSV包實現結構體和csv內容互轉工具詳解

    Go?CSV包實現結構體和csv內容互轉工具詳解

    這篇文章主要介紹了Go?CSV包實現結構體和csv內容互轉工具詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-03-03
  • 適合PHP同學的GoFrame框架使用體驗及學習建議

    適合PHP同學的GoFrame框架使用體驗及學習建議

    這篇文章主要為大家介紹了非常適合PHP同學使用的GoFrame框架設計思想使用體驗及學習建議介紹,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-06-06
  • 深入探索Go語言中unsafe包的使用

    深入探索Go語言中unsafe包的使用

    Go語言的unsafe包被譽為黑科技,它為Go語言提供了底層訪問和操控內存的能力,本文將深入探討Go語言中unsafe包的使用方法和注意事項,需要的可以參考一下
    2023-04-04
  • Go實現數據脫敏的方案設計

    Go實現數據脫敏的方案設計

    在一些常見的業(yè)務場景中可能涉及到用戶的手機號,銀行卡號等敏感數據,對于這部分的數據經常需要進行數據脫敏處理,就是將此部分數據隱私化,防止數據泄露,所以本文給大家介紹了Go實現數據脫敏的方案設計,需要的朋友可以參考下
    2024-05-05

最新評論