Go语言反射

1,变量的介绍

1.变量的内在机制

  • 1.类型信息:这部分是源信息,是预先定义好的
  • 2.值信息:这部分是程序运行过程中,动态改变的

2.反射介绍

1.反射与空接口

  • 1.空接口可以存储任何类型的变量
  • 2.给你一个空接口,怎么知道里面存储的是什么东西
  • 3.在运行时,动态获取一个变量的类型信息和值信息,就叫反射

2.反射的解析

1.内置包:reflect

2.获取类型信息:reflect.TypeOf

1.获取变量类型
package main

import (
    "fmt"
    "reflect"
)

func reflectExample(a interface{})  {
    t := reflect.TypeOf(a)  // 类型相关信息

    k := t.Kind()  // 获取具体类型
    switch k {
    case reflect.Int:
	fmt.Println("a is int")
    case reflect.Float64:
	fmt.Println("a is float64")
    case reflect.Array:
	fmt.Println("a is array")
    default:
	fmt.Println("unknow type")
    }
}

func main()  {
    x := 3.4
    reflectExample(x)
}

3.获取值信息:reflect.ValueOf

1.获取变量类型
package main

import (
    "fmt"
    "reflect"
)

func reflectExample(a interface{})  {
    v := reflect.ValueOf(a)  // 类型相关信息
    t := v.Type()  // 和 reflect.TypeOf 一样
    fmt.Println(t.Kind() == reflect.Float64)
}

func main()  {
    x := 3.4
    reflectExample(x)
}
2.获取变量绑定的值
package main

import (
    "fmt"
    "reflect"
)

func reflectExample(a interface{})  {
    v := reflect.ValueOf(a)  // 类型相关信息
    t := v.Kind()  // 和 reflect.TypeOf 一样
    switch t {
    case reflect.Int:
	value := v.Int()
	fmt.Println("a is int store value is ", value)
    case reflect.Float64:
	value := v.Float()  // 获取变量绑定的值
	fmt.Println("a is int store value is ", value)
    }
}

func main()  {
    x := 3.4
    reflectExample(x)
}
3.设置变量的值
package main

import (
    "fmt"
    "reflect"
)

func reflectExample(a interface{})  {
    v := reflect.ValueOf(a)  // 类型相关信息
    t := v.Kind()  // 和 reflect.TypeOf 一样
    switch t {
    case reflect.Int:
	value := v.Int()
  	fmt.Println("a is int store value is ", value)
    case reflect.Float64:
	v.SetFloat(7.8)
	fmt.Println("set value success")
    case reflect.Ptr:  // 获取指针类型
	v.Elem().SetFloat(6.8)
	/*
	var b *int = new(int)
	*b = 20  // v.Elem()
	 */
    }
}

func main()  {
    x := 3.4
    //reflectExample(x)  // 会在16行报错,x是值类型,所以在函数内部是无法修改到函数外部的变量值的
    reflectExample(&x)
    fmt.Println(x)  // 6.8
}

3.结构体反射

1.获取结构体信息

package main

import (
    "fmt"
    "reflect"
)

func reflectExample(a interface{})  {
    v := reflect.ValueOf(a)  // 类型相关信息
    t := v.Type()  // 和 reflect.TypeOf 一样
    k := t.Kind()
    switch k {
    case reflect.Int:
	fmt.Println("a is int")
    case reflect.Struct:
	fieldsNum := v.NumField()  // 获取结构体元素个数(小写也可以统计到)
	for i := 0; i < fieldsNum; i++ {
      	    field := v.Field(i)  // 根据下标获取字段信息(使用值对象获取)
	    fieldName := t.Field(i).Name  // 获取字段名称(使用类型对象获取)
	    fieldType := field.Type().Kind()  // 获取字段对应的类型
	    fieldValue := field.Interface()  // 获取字段对应的值
	    /*
	    var fieldValue interface{}
            switch fieldType {  // 也可以通过fieldType类型获取
	    case reflect.Int:
		intValue := field.Int()
		fieldValue = intValue
	    case reflect.String:
		strValue := field.String()
		fieldValue = strValue
	    }
	   */
	fmt.Printf("%s: %s --> %v\n", fieldName, fieldType, fieldValue)
    }
    /*
    Name: string - 小李
    Sex: int - 1
    Age: int - 22
     */
    default:
	fmt.Println("default type")
    }
}

type Student struct {
    Name string
    Sex int
    Age int
}

func main()  {
    s := Student{"小李", 1, 22,}
    reflectExample(s)
}
  • v := reflect.ValueOf(a)
    • fieldsNum := v.NumField()
    • field := v.Field(i)
      • fieldType := field.Type().Kind():获取字段名对应的类型
      • fieldValue := field.Interface():获取字段名对应的值
  • t := reflect.TypeOf(a)
    • t.Field(i).Name:获取字段名
2.设置结构体信息
type Student struct {
    Name string
    Sex int
    Age int
}

func main()  {
    s := Student{"小李", 1, 22,}

    v := reflect.ValueOf(&s)  // 类型相关信息
    // v.Elem()  --->  *v
    v.Elem().Field(0).SetString("小张")  // 通过索引设置字段值
    v.Elem().FieldByName("Sex").SetInt(0)  // 通过字段名设置值
    v.Elem().FieldByName("Age").SetInt(18)
    fmt.Println(s)  // {小张 0 18}
}
3.获取结构体绑定的方法
package main

import (
    "fmt"
    "reflect"
)

func reflectExample(a interface{})  {
    v := reflect.ValueOf(a)
    t := v.Tpye()
    k := t.Kind()
    switch k {
    case reflect.Int:
	fmt.Println("a is int")
    case reflect.Ptr:  // 如果传递的是对象而不是引用,则需要在Struct中捕获
        /*
        可以根据指针获取到具体的对象,然后再判断具体对象的类型,从而实现精确赋值
        obj := t.Elem()
        switch obj.Kind() {
        case reflect.Int:
            v.Int(1)
        case reflect.Struct:
            fmt.Println("a is a struct")
        }
        */
	methodNum := t.NumMethod()  // 获取结构体方法个数(小写也可以统计到)
	// t.MethodByName("SetName")  通过方法名获取
        for i := 0; i < methodNum; i++ {
	    method := t.Method(i)  // 得到一个方法的结构体
	    /*
            type Method struct {
		Name	string
		PkgPath	string
		Type	Type
		Func	Value
		Index	Int
	    }
	   */
	methodName := method.Name
	methodType := method.Type  // 获取方法的签名
	fmt.Printf("%s: %v\n", methodName, methodType)  // SetName: func(main.Student, string)
    }
    default:
        fmt.Println("default type")
    }
}

type Student struct {
    Name string
    Sex int
    Age int
}

func (s *Student) SetName(name string) {
    s.Name = name
}


func main()  {
    var s Student
    reflectExample(&s)
}
  • 注意:
    • 1.指针类型的方法,无法直接通过结构对象体获取,需要使用结构体对象地址去获取
      type S struct {}
      func (s *S) Set {}
      
      var s = S
      t := reflect.TypeOf(s)  // 需要 t := reflect.TypeOf(&s)
      t.NumMethod()  // 统计不到 Set 方法
      
    • 2.值类型的方法,传递对象地址或对象都可以获取
    • 3.小写字母开头的方法无法统计
4.调用结构体中的方法
package main

import (
	"fmt"
	"reflect"
)

type Student struct {
    Name string
    Sex int
    Age int
}

func (s *Student) SetName(name string) {
    s.Name = name
}


func main()  {
    var s Student
    v := reflect.ValueOf(&s)
    method := v.MethodByName("SetName")
    // 定义一个空切片
    var args []reflect.Value
    name := "小虎"
    // 将字符串转换为reflect.Value类型
    nameValue := reflect.ValueOf(name)
    // 将reflect.Value类型的字符串添加到reflect.Value类型的切片中
    args = append(args, nameValue)
    // 调用SetName方法,传递参数
    method.Call(args)
    fmt.Println(s)  // {小虎 0 0}
}
5.获取结构体中的tag信息
package main

import (
    "fmt"
    "reflect"
)

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

func main()  {
    var s Student
    t := reflect.TypeOf(&s)

    nameStruct, ok := t.Elem().FieldByName("Name")  // 根据字段名获取字段对象
    if ok {
	fmt.Println(nameStruct.Name)  // Name
	fmt.Println(nameStruct.Tag.Get("json"))  // name
    }

    // 也可以通过索引获取字段信息
    field0 := t.Elem().Field(0)
    fmt.Println(field0.Name)  // Name
    fmt.Println(field0.Tag.Get("json"))  // name
}

4.反射总结级应用场景

1.总结

  • 在运行时动态获取一个变量的类型信息和值信息

2.应用场景

  • 1.序列化和反序列化,比如:jsonprotobuf
  • 2.各种非数据库的ORM,比如:gormsqlx等中间件
  • 3.配置文件解析相关的库,比如:yamlini
posted @ 2022-09-26 20:20  fatpuffer  阅读(46)  评论(0)    收藏  举报