go基础-18.反射

类型判断

判断一个变量是否是结构体,切片,map

package main

import (
  "fmt"
  "reflect"
)

func refType(obj any) {
  typeObj := reflect.TypeOf(obj)
  fmt.Println(typeObj, typeObj.Kind())
  // 去判断具体的类型
  switch typeObj.Kind() {
  case reflect.Slice:
    fmt.Println("切片")
  case reflect.Map:
    fmt.Println("map")
  case reflect.Struct:
    fmt.Println("结构体")
  case reflect.String:
    fmt.Println("字符串")
  }
}

func main() {
  refType(struct {
    Name string
  }{Name: "枫枫"})
  name := "枫枫"
  refType(name)
  refType([]string{"枫枫"})
}

通过反射获取值

func refValue(obj any) {
  value := reflect.ValueOf(obj)
  fmt.Println(value, value.Type())
  switch value.Kind() {
  case reflect.Int:
    fmt.Println(value.Int())
  case reflect.Struct:
    fmt.Println(value.Interface())
  case reflect.String:
    fmt.Println(value.String())

  }
}

通过反射修改值

注意,如果需要通过反射修改值,必须要传指针,在反射中使用Elem取指针对应的值

func refSetValue(obj any) {
  value := reflect.ValueOf(obj)
  elem := value.Elem()
  // 专门取指针反射的值
  switch elem.Kind() {
  case reflect.String:
    elem.SetString("枫枫知道")
  }
}

func main() {
  name := "枫枫"
  refSetValue(&name)
  fmt.Println(name)
}

结构体反射

读取json标签对应的值,如果没有就用属性的名称

package main

import (
  "fmt"
  "reflect"
)

type Student struct {
  Name string
  Age  int `json:"age"`
}

func main() {
  s := Student{
    Name: "枫枫知道",
    Age:  24,
  }
  t := reflect.TypeOf(s)
  v := reflect.ValueOf(s)

  for i := 0; i < t.NumField(); i++ {
    field := t.Field(i)
    jsonField := field.Tag.Get("json")
    if jsonField == "" {
      // 说明json的tag是空的
      jsonField = field.Name
    }
    fmt.Printf("Name: %s, type: %s, json: %s, value: %v\n", field.Name, field.Type, jsonField, v.Field(i))
  }
}

当然了,我们写的这个都很简单了,没有处理-和omitempty的情况

修改结构体中某些值

例如,结构体tag中有big的标签,就将值大写

package main

import (
  "fmt"
  "reflect"
  "strings"
)

type Student struct {
  Name1 string `big:"-"`
  Name2 string
}

func main() {
  s := Student{
    Name1: "fengfeng",
    Name2: "zhangsan",
  }
  t := reflect.TypeOf(s)
  v := reflect.ValueOf(&s).Elem()

  for i := 0; i < t.NumField(); i++ {
    field := t.Field(i)
    bigField := field.Tag.Get("big")
    // 判断类型是不是字符串
    if field.Type.Kind() != reflect.String {
      continue
    }
    if bigField == "" {
      continue
    }
    // 修改值
    valueFiled := v.Field(i)
    valueFiled.SetString(strings.ToTitle(valueFiled.String()))
  }
  fmt.Println(s)
}

调用结构体方法

如果结构体有call这个名字的方法,就执行它

package main

import (
  "fmt"
  "reflect"
)

type Student struct {
  Name string
  Age  int
}

func (Student) See(name string) {
  fmt.Println("see name:", name)
}

func main() {
  s := Student{
    Name: "fengfeng",
    Age:  21,
  }
  t := reflect.TypeOf(s)
  v := reflect.ValueOf(s)

  for i := 0; i < t.NumMethod(); i++ {
    methodType := t.Method(i)
    fmt.Println(methodType.Name, methodType.Type)
    if methodType.Name != "See" {
      continue
    }
    methodValue := v.Method(i)
    methodValue.Call([]reflect.Value{
      reflect.ValueOf("枫枫"), // 注意这里的类型
    })
  }
}

orm的一个小案例

package main

import (
  "errors"
  "fmt"
  "reflect"
  "strings"
)

type Student struct {
  Name string `feng-orm:"name"`
  Age  int    `feng-orm:"age"`
}

type UserInfo struct {
  Id   int    `feng-orm:"id"`
  Name string `feng-orm:"name"`
  Age  int    `feng-orm:"age"`
}

func Find(obj any, query ...any) (sql string, err error) {
  // Find(Student, "name = ?", "fengfeng")
  // 希望能够生成 select name, age from  where  name = 'fengfeng'
  t := reflect.TypeOf(obj)
  //v := reflect.ValueOf(obj)
  // 首先得是结构体对吧
  if t.Kind() != reflect.Struct {
    err = errors.New("非结构体")
    return
  }
  // 拿全部字段

  // 拼接条件
  // 第二个参数,中的问号,就决定后面还能接多少参数
  var where string
  if len(query) > 0 {
    // 有第二个参数,校验第二个参数中的?个数,是不是和后面的个数一样
    q := query[0] // 理论上还要校验第二个参数的类型
    if strings.Count(q.(string), "?")+1 != len(query) {
      err = errors.New("参数个数不对")
      return
    }
    // 拼接where语句
    // 将?号带入后面的参数
    for _, a := range query[1:] {
      // 替换q
      // 这里要判断a的类型
      at := reflect.TypeOf(a)
      switch at.Kind() {
      case reflect.Int:
        q = strings.Replace(q.(string), "?", fmt.Sprintf("%d", a.(int)), 1)
      case reflect.String:
        q = strings.Replace(q.(string), "?", fmt.Sprintf("'%s'", a.(string)), 1)
      }
    }
    where += "where " + q.(string)
  }
  // 如果没有第二个参数,就是查全部

  // 拼接select
  // 拿所有字段,取feng-orm对应的值
  var columns []string
  for i := 0; i < t.NumField(); i++ {
    field := t.Field(i)
    f := field.Tag.Get("feng-orm")
    // 不考虑是空的情况
    columns = append(columns, f)
  }

  // 结构体的小写名字+s做表名
  name := strings.ToLower(t.Name()) + "s"

  // 拼接最后的sql
  sql = fmt.Sprintf("select %s from %s %s", strings.Join(columns, ","), name, where)
  return
}

func main() {
  sql, err := Find(Student{}, "name = ? and age = ?", "枫枫", 23)
  fmt.Println(sql, err) // select name,age from students where name = '枫枫' and age = 23
  sql, err = Find(UserInfo{}, "id = ?", 1)
  fmt.Println(sql, err) // select id,name,age from userinfos where id = 1
}

对反射的一些建议

如果是写一下框架,偏底层工具类的操作

不用反射确实不太好写,但是如果是在业务上,大量使用反射就不太合适了

因为反射的性能没有正常代码高,会慢个一到两个数量级

使用反射可读性也不太好,并且也不能在编译期间发生错误

参考文档

go反射 Go 反射-CSDN博客

反射 Go语言基础之反射 | 李文周的博客

解析sql案例

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值