首页 > 其他分享 >go基础-18.反射

go基础-18.反射

时间:2024-09-24 11:16:15浏览次数:8  
标签:反射 name 18 fmt reflect Println go Name string

类型判断

判断一个变量是否是结构体,切片,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反射 https://blog.csdn.net/a1053765496/article/details/129904985

反射 https://www.liwenzhou.com/posts/Go/reflect/

解析sql案例

标签:反射,name,18,fmt,reflect,Println,go,Name,string
From: https://www.cnblogs.com/fengfengzhidao/p/18428757

相关文章

  • go基础-20.部署
    go项目的部署特别简单,编写完成之后,只需要执行gobuild即可打包为可执行文件注意,这个操作是不同平台不一样的windows下打包就是exe文件,linux下打包就是二进制文件打包命令gobuild打当前目录下的main包,注意,只能有一个main函数的包gobuildxxx.go打当前目录下,xxx.go的......
  • go基础-3.基本数据类型
    go语言的基本数据类型有整数形浮点型复数布尔字符串整数型go语言的整数类型,具体细分有很多varn1uint8=2varn2uint16=2varn3uint32=2varn4uint64=2varn5uint=2varn6int8=2varn7int16=2varn8int32=2varn9int64=2va......
  • go基础-2.变量定义与输入输出
    变量定义标准的变量定义packagemainimport"fmt"funcmain(){//先定义,再赋值varnamestringname="枫枫"fmt.Println(name)//var变量名类型="变量值"varuserNamestring="枫枫"fmt.Println(userName)}如果一个变量定义了......
  • go基础-5.判断语句
    if语句以年龄为例,输入的年龄在某一个区间,就输出对应的提示信息<=0未出生1-18未成年18-35青年>=35中年很明显,这是一个多选一的情况我们有很多中方式来实现中断式packagemainimport"fmt"funcmain(){fmt.Println("请输入你的年龄:")varagei......
  • go基础-4.数组、切片、map
    数组数组(Array)是一种非常常见的数据类型,几乎所有的计算机编程语言中都会用到它数组里的元素必须全部为同一类型,要嘛全部是字符串,要嘛全部是整数声明数组时,必须指定其长度或者大小packagemainimport"fmt"funcmain(){vararray[3]int=[3]int{1,2,3}fmt.Pr......
  • go基础-7.函数和指针
    函数是一段封装了特定功能的可重用代码块,用于执行特定的任务或计算函数接受输入(参数)并产生输出(返回值)函数定义packagemainimport"fmt"//使用func关键字定义一个函数funcsayHello(){fmt.Println("hello")}funcmain(){//函数()调用函数sayHello()}......
  • go基础-6.for循环
    任何编程语言,都会有for循环,它的一般写法是for初始化;条件;操作{}例如求1+2+...+100的和packagemainimport"fmt"funcmain(){varsum=0fori:=0;i<=100;i++{sum+=i}fmt.Println(sum)}for循环的五种变体传统for循环如上死循环每......
  • go基础-8.init函数和defer函数
    init函数init()函数是一个特殊的函数,存在以下特性:不能被其他函数调用,而是在main函数执行之前,自动被调用init函数不能作为参数传入不能有传入参数和返回值一个go文件可以有多个init函数,谁在前面谁就先执行packagemainimport"fmt"funcinit(){fmt.Println("init1"......
  • go基础-11.接口
    接口是一组仅包含方法名、参数、返回值的未具体实现的方法的集合packagemainimport"fmt"//Animal定义一个animal的接口,它有唱,跳,rap的方法typeAnimalinterface{sing()jump()rap()}//Chicken需要全部实现这些接口typeChickenstruct{Namestring}......
  • go基础-10.自定义数据类型
    在Go语言中,自定义类型指的是使用type关键字定义的新类型,它可以是基本类型的别名,也可以是结构体、函数等组合而成的新类型。自定义类型可以帮助我们更好地抽象和封装数据,让代码更加易读、易懂、易维护自定义类型结构体就是自定义类型中的一种除此之外我们使用自定义类型,还可......