衢州婚介:golang--深入简出,带你用golang的反射撸一个公用后台查询方法

admin/2020-04-14/ 分类:科技/阅读:

一些基本方式

本篇不会先容反射的基本概念和原理等,会从每个常用的方式入手,解说一些基本和进阶用法,反射不太适合在营业层使用,由于会几何倍的降低运行速率,而且用反射做出来的程序结实度不高,一旦一个环节没有处置好就会直接panic,影响程序的运行,然则在后台上使用照样很适合的,可以极大的降低代码量,从繁复的增删改查操作和无边的抛err(面向错误编程,太贴切了)中解脱出来。

reflect.TypeOf()

可以获取任何变量的类型工具,使用该工具可以获取变量的NameKindName代表的是变量类型的名称,Kind代表的是变量的底层类型名称,以下是两个典型的例子。

// 系统变量 str := "张三" reflectType := reflect.TypeOf(str) fmt.Printf("name: %v kind: %v", reflectType.Name(), reflectType.Kind()) // name: string kind: string // 自界说变量 type person string a := person("张三") reflectType := reflect.TypeOf(a) fmt.Printf("name: %v kind: %v", reflectType.Name(), reflectType.Kind()) // name: person kind: string 

Elem()方式

主要用来获取指针类型(只能使用在数组、chan、map、指针、切片几个类型上)的类型工具

str := "张三" reflectType := reflect.TypeOf(&str) reflectElem := reflectType.Elem() fmt.Printf("name: %v kind: %v", reflectElem.Name(), reflectElem.Kind()) // name: string kind: string 

reflect.ValueOf()

可以获取随便变量的值工具,它的类型是reflect.Value,使用该工具同样可以获取变量的NameKind,通过获取Kind可以使用类型断言获取变量的值。

这里的reflect.ValueOf实在作用不大,在现实应用场景中多先使用reflect.ValueOf获取变量的reflect.Value然后接Interface()方式把变量转化为Interface{}类型,获取reflect.Value的方式多接纳reflect.TypeOf()reflect.New()方式,后面实战部门会有详细用法。

isNil()

判断值工具是否为nil,只能对通道、切片、数组、map、函数、interface等使用。

isValid()

判断值工具是否为有效值,即非其默认0值,例如数字类型的0,字符串类型的"",在现实使用中,若是纰谬这些值举行处置,可能会直接panic。

reflect.SliceOf()

配合reflect.TypeOf返回单个类型的切片类型。

str := "张三" reflectType := reflect.TypeOf(str) reflectSlice := reflect.SliceOf(reflectType) fmt.Printf("name: %v kind: %v", reflectSlice.Name(), reflectSlice.Kind()) // name: kind: slice // 获取切片中元素的值 a := []int{8, 9, 10} reflectType := reflect.ValueOf(a) for i := 0; i < reflectType.Len(); i { fmt.Println(reflectType.Index(i)) } // 8 9 10 

这里注重数组、指针、切片、map等一些类型是没有类型名称的。

reflect.New()

配合reflect.TypeOf实例化一个该类型的值工具,返回该值工具的指针(想要使用反射设置值,必须使用指针)。

str := "张三" reflectType := reflect.TypeOf(str) reflectValue := reflect.New(reflectType) // 设置值 reflectValue.Elem().SetString("李四") fmt.Printf("value: %v kind: %v", reflectValue.Elem(), reflectValue.Elem().Kind()) // value: 李四 kind: string 

reflect.PtrTo()

返回值工具的指针。

str := "张三" reflectType := reflect.TypeOf(str) if reflectType.Kind() != reflect.Ptr { reflectType = reflect.PtrTo(reflectType) } fmt.Printf("value: %v kind: %v", reflectType, reflectType.Kind()) // value: *string kind: ptr 

结构体的反射

上面的几个方式只是开胃菜,真正常用仍然是结构体的反射,营业中种种增删改查操作都要通过数据库完成,而数据库交互使用的都是结构体,这里会先列出一些结构体反射要用到的方式,然后通过一篇后台公用model类的实战来完成这篇的内容。

和上面几个基本方式有关的内容这里就不再赘述,有兴趣的可以自己私底下去试试,这里只针对一些结构体的专用方式举行说明。

结构体字段相关的几种方式

NumField()

返回结构体的字段数目,NumField()使用的工具必须是结构体,否则会panic。

type Student struct { Name string Age int } a := &Student{ Name: "张三", Age: 18, } reflectValue := reflect.ValueOf(a) fmt.Println(reflectValue.Elem().NumField()) // 2 

Field()

通过字段的索引获取字段的值,从0最先,顺序参照结构体界说时的由上到下的顺序。

a := &Student{ Name: "张三", Age: 18, } reflectValue := reflect.ValueOf(a) for i := 0; i < reflectValue.Elem().NumField(); i { fmt.Println(reflectValue.Elem().Field(i)) } 

FieldByName()

通过字段名称获取字段的值。

a := &Student{ Name: "张三", Age: 18, } reflectValue := reflect.ValueOf(a) fmt.Println(reflectValue.Elem().FieldByName("Name")) // 张三 

NumMethod()

返回结构体的方式数目。

FieldByNameFunc()

凭据传入的匿名函数返回对应名称的方式。

Method()

直接通过方式的索引,返回对应的方式。

MethodByName()

通过方式名称返回对应的方式。

以上四个方式相关的函数就不放例子了,通过对应的函数获取到方式后,使用Call()举行挪用,其中特别注重的是,挪用时传入的参数必须是[]reflect.Value花样的。

实战篇一:编写一个公用的后台查询方式

这里用到的数据库类为gorm本篇不探讨其相关知识,若有疑惑,请自行实践。

首先编写model,根目录下建立文件夹model,在model文件夹中建立search.go

// Student 学生 type Student struct { Name string Age int ID int } // TableName 表名 func (Student) TableName() string { return "student" } 

编写实现公用方式的接口,根目录下建立search.go

// SearchModel 搜索接口 type SearchModel interface { TableName() string } // SearchModelHandler 存储一些查询过程中的需要信息 type SearchModelHandler struct { Model SearchModel } // GetSearchModelHandler 获取处置器 func GetSearchModelHandler(model SearchModel) *SearchModelHandler { return &SearchModelHandler{ Model: model, } } // Search 查找 func (s *SearchModelHandler) Search() string { query := db.model(s.Model) itemPtrType := reflect.TypeOf(s.Model) if itemPtrType.Kind() != reflect.Ptr { itemPtrType = reflect.PtrTo(itemPtrType) } itemSlice := reflect.SliceOf(itemPtrType) res := reflect.New(itemSlice) // 这一步至关重要,虽然Scan方式吸收的是一个interface{}类型,然则由于我们这里传入的SearchModel,若是直接使用s.Model执行传入会报错 // 缘故原由在于这里的Scan的interface和我们传入的model实现的是差别的接口,Scan只熟悉gorm包中界说的接口类型 err := query.Scan(res.Interface()).Error if err != nil { // 这里不要学我 panic("error") } ret, _ := json.Marshal(res) return string(ret) } 

就这样一个简朴的公用类就诞生了,接下来就是挪用了,在更目录下建立main.go

func main() { handler := GetSearchModelHandler(&model.Student{}) handler.Search() } 

实战进阶篇:为单个表添加上附加信息

好比我们另有一个班级表,而在返回学生信息的时刻需要加上班级信息,这该怎么操作呢,这里我只提供自己的一种思绪,若是有更好的建议,请写在下方的谈论里 配合交流。

首先,建立class的结构体,在model文件夹内建立class.go

// Class 班级 type Class struct { ID int Name string } // TableName 表名 func (Class) TableName() string { return "class" } 

然后编写一个公用的接口,在model文件夹下建立文件additional_api.go

// AdditionalInfo 附加信息获取辅助 type AdditionalInfo struct { FieldName string Method func(ids []int32) string } // MinMapAPI 获取总内容接口,相当于实战一中的SearchModel type MinMapAPI interface { TableName() string } // MinMapInterface 最小信息获取接口 type MinMapInterface interface { TransFields() string } 

上面的方式先界说好,后面有用,然后修改model的内容,打开class.go输入

// ClassMin 最小班级信息 type ClassMin struct { ID int Name string } // TransFields 转换名称,填写你要获取的字段的名称 func (c *ClassMin) TransFields() string { return "Name" } 

接下来编写详细获取附加信息的方式,打开additional_api.go,输入以下内容

// GetMinMap 获取最小信息 func GetMinMap(ids []int32, model MinMapAPI, minModel MinMapInterface) string { // 获取总数据的切片 modelType := reflect.TypeOf(model) modelSliceType := reflect.SliceOf(modelType) res := reflect.New(modelSliceType) err := db.Model(model).Where("id in (?)", ids).Scan(res.Interface()).Error if err != nil { panic("error") } minModelType := reflect.TypeOf(minModel).Elem() resValue := res.Elem() resLen := resValue.Len() ret := make(map[int]MinMapInterface, resLen) for i := 0; i < resLen; i { // 获取当前下标的数据 item := resValue.Index(i).Elem() // 获取要获得的字段 name := item.FieldByName(minModel.TransFields()) id := item.FieldByName("ID") // 拼接返回值 setItem := reflect.New(minModelType) setItem.Elem().FieldByName("ID").SetInt(int64(id.Interface().(int))) setItem.Elem().FieldByName(minModel.TransFields()).SetString(name.Interface().(string)) // 查询出来的内容是详细的model,这里类型断言转化回去 ret[id.Interface().(int)] = setItem.Interface().(MinMapInterface) } data, _ := json.Marshal(ret) return string(data) } 

修改student.go,加上获取附加数据的方式,这里使用了一个匿名函数,既保证了每个model都有其独占的参数,也保证了代码的复用性

// AdditionalParams 附加数据参数 func (s *Student) AdditionalParams() map[string]AdditionalInfo { return map[string]AdditionalInfo{ "class": { FieldName: "ClassID", Method: func(ids []int32) string { return GetMinMap(ids, &Class{}, &ClassMin{}) }, }, } } 

响应的,也要修改search.go,为捏词添加上AdditionalParams方式,这里直接贴上search.go的最终代码以供比对

// SearchModel 搜索接口 type SearchModel interface { TableName() string AdditionalParams() map[string]model.AdditionalInfo } // SearchModelHandler 存储一些查询过程中的需要信息 type SearchModelHandler struct { Model SearchModel ListValue reflect.Value AdditionalData string } // GetSearchModelHandler 获取处置器 func GetSearchModelHandler(model SearchModel) *SearchModelHandler { return &SearchModelHandler{ Model: model, } } // Search 查找 func (s *SearchModelHandler) Search() interface{} { query := db.model(s.Model) itemPtrType := reflect.TypeOf(s.Model) if itemPtrType.Kind() != reflect.Ptr { itemPtrType = reflect.PtrTo(itemPtrType) } itemSlice := reflect.SliceOf(itemPtrType) res := reflect.New(itemSlice) // 这一步至关重要,虽然Scan方式吸收的是一个interface{}类型,然则由于我们这里传入的SearchModel,若是直接使用s.Model执行传入会报错 // 缘故原由在于这里的Scan的interface和我们传入的model实现的是差别的接口,Scan只熟悉gorm包中界说的接口类型 err := query.Scan(res.Interface()).Error if err != nil { // 这里不要学我 panic("error") } s.ListValue = res.Elem() data, _ := json.Marshal(res) ret := map[string]string { "list": string(data), "additional": s.AdditionalData, } return ret } // GetAdditionalData 获取附加信息 func (s *SearchModelHandler) GetAdditionalData() { additionParams := s.Model.AdditionalParams() list := s.ListValue listLen := list.Len() if len(additionParams) < 1 || list.Len() < 1 { s.AdditionalData = "" return } additionalIDs := make(map[string][]int) additionalData := make(map[string]string, len(additionParams)) for i := 0; i < listLen; i { for key, val := range additionParams { fieldName := val.FieldName // 判断Map中的键是否已存在 if _, ok := additionalIDs[key]; !ok { additionalIDs[key] = make([]int, 0, listLen) } fields := list.Index(i).Elem().FieldByName(fieldName) if !fields.IsValid() { continue } additionalIDs[key] = append(additionalIDs[key], fields.Interface().(int)) } } for k, v := range additionalIDs { additionalData[k] = additionParams[k].Method(v) } ret, _ := json.Marshal(additionalData) s.AdditionalData = string(ret) } 
,

Sunbet 申博

Sunbet 申博www.baodingxsls.com Sunbet是菲律宾娱乐的官方网站。Sunbt官网有你喜欢的Sunbet、申博APP下载、菲律宾娱乐最新网址、菲律宾娱乐管理网最新网址等。

TAG:
阅读:
广告 330*360
广告 330*360

热门文章

HOT NEWS
  • 周榜
  • 月榜
Sunbet_进入申博sunbet官网
微信二维码扫一扫
关注微信公众号
新闻自媒体 Copyright © 2002-2019 Sunbet 版权所有
二维码
意见反馈 二维码