我想让你知道:
1、反射可以将interface类型变量转换称反射对象
2、反射可以将反射对象还原成interface对象
3、反射对象可以修改,value值必须是可设置的
那么什么是反射呢?接下来就让我们开启反射之旅吧
一、什么是反射?
在计算机科学领域,反射是指一类应用,它们能够自描述和自控制。也就是说,这类应用通过采用某种机制来实现对自己行为的描述(self-representation)和监测(examination),
并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。每种语言的反射模型都不同,并且有些语言根本不支持反射。Golang语言实现了反射,
反射机制就是在运行时动态的调用对象的方法和属性,官方自带的reflect包就是反射相关的,只要包含这个包就可以使用。
二、反射的使用场景
1、动态初始化结构体数值
通常我们做业务开发的时候,需要定义表字段的状态值,如果一个个手动设置很麻烦!
那么我们通过反射来动态初始化字段的状态值,做到状态统一管理。比如订单的状态会有多个
case,那么接下来咱们用反射来实现多个状态case初始化
package main
import (
"fmt"
"github.com/stoewer/go-strcase"
"reflect"
)
type orderState struct {
Closed string
WaitPay string
Paid string
Cancel string
}
var OrderStates orderState
func main() {
v := reflect.ValueOf(&OrderStates).Elem()
//订单状态动态初始化
initStrAttr(v)
fmt.Println(OrderStates)
//out {closed wait_pay paid cancel}
}
func initStrAttr(v reflect.Value) {
for i := 0; i < v.NumField(); i++ {
t := v.Field(i).Kind()
name := v.Type().Field(i).Name
if v.Field(i).Kind() == reflect.String {
v.Field(i).SetString(strcase.SnakeCase(name))
} else {
panic(fmt.Sprintf("结构体字段 %s 类型必须为字符串,而不是%s", name, t))
}
}
}
2、数据导出到excel
每次产品让导出不同表数据的时候,总要写一套导出的逻辑,干着吃力不讨好事情,没意思!那么有没有满足万能导出的方案呢? 我说有,可以通过反射来实现!看下面的案例,教你如果写出万能导出数据到excel代码。
package main
import (
"fmt"
"github.com/xuri/excelize/v2"
"math/rand"
"reflect"
"time"
)
type DocumentRow struct {
Id string `json:"id" h:"章节ID"` //章节ID
Name string `json:"name" h:"标题"` //标题
Author string `json:"author" h:"作者"` //作者
Logo string `json:"logo" h:"封面图片"` //封面图片
LogoUrl string `json:"logo_url" h:"封面地址"` //封面地址
Description string `json:"description" h:"文章描述"` //文章描述
Type string `json:"type" h:"类型"` //类型
TypeValue string `json:"type_value" h:"类型值"` //类型值
Status string `json:"status" h:"状态"` //状态
StatusValue string `json:"status_value" h:"状态值"` //状态值
CreatorName string `json:"creator_name" h:"创建人"` //创建人
OnAt string `json:"on_at" h:"上架时间"` //上架时间
Views int `json:"views" h:"章节浏览次数"` //章节浏览次数
Weight int `json:"weight" h:"排序权重"` //排序权重
}
func getData(num int) []DocumentRow {
var list = make([]DocumentRow, 0, num)
for i := 0; i < num; i++ {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
no := r.Intn(9999999-8888888) + 8888888
list = append(list, DocumentRow{
Id: fmt.Sprintf("%d", i),
Name: fmt.Sprintf("文章_%d", no),
Author: "张三",
Logo: "",
LogoUrl: "",
Description: "",
Type: "",
TypeValue: "",
Status: "",
StatusValue: "",
CreatorName: "",
OnAt: "",
Views: 0,
Weight: 0,
})
}
return list
}
func main() {
data := getData(100)
ExportExcel("./document.xlsx", "文章列表", data)
}
// ExportExcel 数据导出到excel
// filePath 保存的路径
// sheetName 工作标名称
// data 要导的数据
func ExportExcel(filePath, sheetName string, data interface{}) {
v := reflect.ValueOf(data)
if v.Kind() != reflect.Slice {
panic("not slice")
}
if v.Len() == 0 {
return
}
f := excelize.NewFile()
err := f.SetSheetName("Sheet1", sheetName)
if err != nil {
panic(err)
}
t := v.Index(0).Type()
heads := make([]interface{}, 0, t.NumField())
for i := 0; i < v.Len(); i++ {
item := make([]interface{}, 0, t.NumField())
for j := 0; j < t.NumField(); j++ {
if i == 0 {
name := t.Field(j).Tag.Get("h")
heads = append(heads, name)
}
item = append(item, v.Index(i).Field(j))
}
if i == 0 {
if err = f.SetSheetRow(sheetName, fmt.Sprintf("A%d", 1), &heads); err != nil {
panic(err)
}
}
if err = f.SetSheetRow(sheetName, fmt.Sprintf("A%d", i+2), &item); err != nil {
panic(err)
}
}
if err = f.SaveAs(filePath); err != nil {
panic(err)
}
}
3、excel导入到数据库
我们经常需要将不同的excel数据导入到不同表中,每次都要写不同导入逻辑,烦都烦死了,那么咱们我怎么写一个通用的 将excel导入逻辑呢?以后接入新的导入需求,直接调用它就可以了,下面咱们就用反射实现它。
package reflects
import (
"github.com/xuri/excelize/v2"
"reflect"
"strings"
)
type Title struct {
Index int //当前行记录第i列索引下标
Field string //struct 字段名字
FieldIndex int //struct 字段下标
Name string //excel 头部标题
}
func ImportDb(filePath string, data any) {
v := reflect.ValueOf(data).Elem()
if v.Kind() != reflect.Slice {
panic("hello")
}
t := v.Type().Elem()
f, err := excelize.OpenFile(filePath)
if err != nil {
println(err.Error())
return
}
// 获取 Sheet1 上所有单元格
rows, err := f.GetRows("Sheet1")
if len(rows) == 0 {
panic("暂无数据")
}
if t.NumField() != len(rows[0]) {
panic("表格头字段缺失")
}
titles := make([]Title, 0, len(rows[0]))
for i := 0; i < len(rows[0]); i++ {
tmp := Title{
Index: i,
Field: "",
Name: rows[0][i],
}
//匹配头部标题
for j := 0; j < t.NumField(); j++ {
if strings.Trim(tmp.Name, " ") == strings.Trim(t.Field(j).Tag.Get("h"), " ") {
tmp.Field = t.Field(j).Name
tmp.FieldIndex = j
}
}
titles = append(titles, tmp)
}
for i := 1; i < len(rows); i++ {
info := reflect.New(t).Elem()
for j := 0; j < len(titles); j++ {
value := ""
if len(rows[i]) > titles[j].Index {
value = rows[i][titles[j].Index]
}
f := info.Field(titles[j].FieldIndex)
switch f.Kind() {
case reflect.String:
f.SetString(value)
case reflect.Ptr:
}
}
v.Set(reflect.Append(v, info))
}
}
type HelpApplyListRow struct {
Id string `json:"id" h:"ID"`
Title string `json:"title" h:"援助室名称"` //援助室名称
PatientName string `json:"patient_name" h:"被援助人"` //被援助人
Content string `json:"content" h:"援助内容"` //援助内容
OrganizationId string `json:"organization_id" h:"申请机构id"` //申请机构id
OrganizationName string `json:"organization_name" h:"申请机构"` //申请机构
Status string `json:"status" h:"申请状态"` //申请状态
StatusValue string `json:"status_value" h:"申请状态映射值"` //申请状态映射值
State string `json:"state" h:"援助状态"` //援助状态
StateValue string `json:"state_value" h:"援助状态映射值"` //援助状态映射值
CreatorName string `json:"creator_name" h:"申请人姓名"` //申请人姓名
Reason string `json:"reason" h:"拒绝原因"` //拒绝原因
StartAt *gtime.Time `json:"start_at" h:"开始时间"` //开始时间
EndAt *gtime.Time `json:"end_at" h:"结束时间"` //结束时间
}
func main() {
//数据导入到excel
//data := getData(100)
//reflects.ExportExcel("./document.xlsx", "文章列表", data)
//
//data2 := getData2(1000)
//reflects.ExportExcel("./applyhelp.xlsx", "援助申请", data2)
path, err := os.Getwd()
if err != nil {
panic(err)
}
var list []HelpApplyListRow
reflects.ImportDb(path+"/applyhelp.xlsx", &list)
}
好了,以上就是我介绍反射的使用场景。