你們好,我叫謝偉,是一名程序員。程序員
近期會持續更新內置庫的學習內容,主要的參考文獻是 官方文檔 和 源代碼。數據庫
本節的主題是:反射 -- 採用某種機制來實現對本身行爲的描述和檢測,是類型的一種能力, 簡單的說是可以獲取數據的類型和值。json
因此反射的核心包括兩方面:類型(type)、值(value)bash
大綱:學習
既然反射的用法包括兩方面,那麼平常編寫代碼過程當中,包括兩個方面:獲取類型、獲取值ui
下面演示最基本的用法,並且多用在結構體這種數據類型上。spa
var number int
number = 1
fmt.Println(reflect.TypeOf(number), reflect.ValueOf(number))
>> int 1
複製代碼
type numberExample int
var numberOther numberExample
numberOther = 2
fmt.Println(reflect.TypeOf(numberOther), reflect.ValueOf(numberOther), reflect.ValueOf(numberOther).Kind())
>> main.numberExample 2 int
複製代碼
能夠看到,如何獲取數據類型,也能夠看出 TypeOf 和 Kind 的區別,TypeOf 獲取的是基本數據類型和以 type 定義的類型;Kind 獲取的是底層的基本的數據類型,但不包括以 type 定義的類型。設計
可是最多見的關於反射的用法仍是對結構體的處理。結構體在 Go 裏面是各類數據類型的數據的集合,同時還能夠具備本身的方法。code
結構體定義,還存在 tag, 在結構體和 json 相互序列化和反序列化之間的起做用。orm
定義以下結構體:
type Info struct {
Name string `json:"name"`
Age interface{} `json:"age"`
Prince float32 `json:"prince"`
}
type Groups struct {
ID uint `json:"id"`
Name string `json:"name"`
Count int `json:"count"`
CanFly bool `json:"can_fly"`
GroupIDs []int `json:"group_ids"`
GroupName []string `json:"group_name"`
Info `json:"info"`
}
複製代碼
定義了兩個結構體,相應的字段也聲明瞭 tag。
初始化:
// 匿名結構體,定義全局變量
var globalValue struct {
groups Groups
}
var valid struct {
tag string
value Model
}
func init() {
globalValue.groups = Groups{
ID: 1,
Name: "xieWei",
Count: 12,
CanFly: false,
GroupIDs: []int{100, 200, 300, 400},
GroupName: []string{"what", "how", "when", "why"},
Info: Info{
Name: "XieXiaoLu",
Age: 20,
Prince: 1.2345,
},
}
valid.tag = "xieWei"
valid.value = Model{
ID: 1,
Count: 2000,
Name: "Golang",
}
}
複製代碼
給結構體定義兩個方法,主要操做 類型和值。
func (g Groups) TypeHandler() {
}
func (g Groups) ValueHandler() {
}
複製代碼
對結構體的反射操做,能夠獲取結構體的屬性的類型、值和 tag。
本身思考下,獲取屬性的類型、值和 tag, 做者會設計些什麼內容?
獲取屬性、遍歷屬性、屬性的數目、按索引獲取屬性
func (g Groups) TypeHandler() {
}
複製代碼
func (g Groups) TypeHandler() {
typeGroups := reflect.TypeOf(g)
name := typeGroups.Name()
fmt.Println("Name: ", name, "Kind", typeGroups.Kind())
for i := 0; i < typeGroups.NumField(); i++ {
filed := typeGroups.Field(i)
fmt.Println(filed.Name, "\t", filed.Tag, "\t", reflect.ValueOf(filed), "\t", filed.Type)
}
for i := 0; i < typeGroups.NumField(); i++ {
filedByIndex := typeGroups.FieldByIndex([]int{i})
filedByName, _ := typeGroups.FieldByName(filedByIndex.Name)
fmt.Println(filedByIndex, filedByIndex.Name, filedByIndex.Type)
fmt.Println(filedByName, filedByName.Name, filedByName.Type)
}
for i := 0; i < typeGroups.NumMethod(); i++ {
method := typeGroups.Method(i)
fmt.Println(method.Name, method.Type)
}
}
複製代碼
操做結構體的方法:
for i := 0; i < typeGroups.NumMethod(); i++ {
method := typeGroups.Method(i)
fmt.Println(method.Name, method.Type)
}
複製代碼
操做值:
func (g Groups) ValueHandler() {
}
複製代碼
func (g Groups) ValueHandler() {
valueGroup := reflect.ValueOf(g)
fmt.Println(valueGroup.NumField(), valueGroup.NumMethod(), valueGroup, reflect.ValueOf(&g).Elem())
for i := 0; i < valueGroup.NumField(); i++ {
field := valueGroup.Field(i)
fmt.Println(field, field.Type(), field.Kind())
}
method := valueGroup.MethodByName("TypeHandler")
fmt.Println(method, method.Kind(), method.Type())
for i := 0; i < valueGroup.NumMethod(); i++ {
method := valueGroup.Method(i)
fmt.Println(method.Type())
}
ref := reflect.ValueOf(&g).Elem()
fmt.Println(ref.FieldByName("Name"), ref.Field(0))
}
複製代碼
爲何是這樣的操做?
屬性、值、遍歷屬性、遍歷值
爲何這麼操做?
那固然看具體的 Type 的定義,是個接口。
type Type interface {
Method(int) Method
MethodByName(string) (Method, bool)
NumMethod() int
Name() string
Kind() Kind
Elem() Type
Field(i int) StructField
FieldByIndex(index []int) StructField
FieldByName(name string) (StructField, bool)
FieldByNameFunc(match func(string) bool) (StructField, bool) NumField() int } 複製代碼
能夠看到,如何操做結構體屬性的類型。
具體的 Value 的定義,是個結構體。無屬性,有方法。
type Value struct {
// contains filtered or unexported fields
}
func (v Value) Field(i int) Value func (v Value) FieldByIndex(index []int) Value func (v Value) FieldByName(name string) Value func (v Value) FieldByNameFunc(match func(string) bool) Value func (v Value) Method(i int) Value func (v Value) MethodByName(name string) Value func (v Value) NumField() int func (v Value) NumMethod() int 複製代碼
有時候,咱們記不住 API,不知道哪些方法可使用,怎麼辦?
以結構體爲例?
能夠看出,嚴格上講,結構體的知識點就屬性(私有、公有), 方法(調用、聲明)。
因此看出,做者底層的結構體的定義也是關於這些的操做。
至此,咱們始終沒有操做 結構體的 tag。
好比下面的結構體定義:
type Model struct {
ID uint `xieWei:"number,max=10,min=1"`
Name string `xieWei:"string"`
Count int `xieWei:"number,max=100,min=1"`
CanFly bool `xieWei:"bool,default=false"`
}
複製代碼
咱們常常看到在 gin 或者 gorm 內看到這些 tag的使用。
好比:gin 中
type PostParam string {
ID uint `form:"id" binding:"required,omitempty"`
Name string `form:"name" binding:"required"`
Number int `form:"number" binding:"required,eq=1|eq=2"`
}
複製代碼
上文根據 tag 規定字段是否必須,空值是否省略,值的範圍
再好比:gorm 定義數據庫表
type Student struct {
Name string `gorm:"type:"varchar,column:name" json:"name"`
Number int `gorm:"type:"integer,column:number" json:"number"`
}
複製代碼
上文根據 tag 規定字段的類型,表列的名稱。
那是如何作到的呢?
答案:反射
經過反射,獲取到結構體的 tag, tag 是個字符串,按照字符串的操做,好比分割操做,獲取到類型等。
好比咱們須要本身完成結構體的屬性的類型的檢驗。
func (m Model) Handler(name string) bool {
typeModel := reflect.TypeOf(m)
if tag, ok := typeModel.FieldByName(name); ok {
if ok := strings.HasPrefix(string(tag.Tag), valid.tag); ok {
//fmt.Println(validTagList[0])
validTagList := strings.FieldsFunc(string(tag.Tag), func(r rune) bool {
return r == ',' || r == '"'
})
switch validTagList[1] {
case "number":
{
fmt.Println(validTagList[1:])
}
case "string":
fmt.Println(validTagList[1:])
case "bool":
fmt.Println(validTagList[1:])
}
} else {
return false
}
}
return false
}
>>
[number max=10 min=1]
[string]
[number max=100 min=1]
[bool default=false]
[number min=1 max=1000]
複製代碼
再進行後續的操做便可。
總體的思路是:
valid:number,max=10,min=1
, 統一按這樣的操做,方便或許的解析。總結:
反射是程序關於自身類型檢測的一種能力,經過內置庫的 reflect 能夠獲取到變量、結構體的類型和值,還能夠設置相應的值。
關於結構體的反射是使用 reflect 的一個比較核心的用處。
如何操做:
後記:學習,總想一口氣所有掌握知識,實際上不科學,第一次看,你可能只能掌握 10%, 正確的作法,應該是反覆看,尤爲是你須要解決問題的時候。最後必定要融入本身的思考,僅僅只是塗塗畫畫寫寫,都能給你增長更多的記憶信息。