• go 反射 reflect 包


    go 反射 reflect 包

    1. 反射介绍

    反射就是程序能够在运行时检查变量和值,求出它们的类型

    反射就是在运行时动态的获取一个变量的类型信息和值信息

    反射是指在程序运行期对程序本身进行访问和修改的能力。程序在编译时,变量被转换为内存地址,变量名不会被编译器写入到可执行部分。在运行程序时,程序无法获取自身的信息。

    支持反射的语言可以在程序编译期将变量的反射信息,如字段名称、类型信息、结构体信息等整合到可执行文件中,并给程序提供接口访问反射信息,这样就可以在程序运行期获取类型的反射信息,并且有能力修改它们。

    Go程序在运行期使用reflect包访问程序的反射信息。

    2. 变量的内在机制

    Go语言中的变量是分为两部分的:

    • 类型信息:预先定义好的元信息。
    • 值信息:程序运行过程中可动态变化的。

    3. 反射的实际用途

    3.1 通过函数实现sql生成

    import "fmt"
    
    type order struct {
    	orderId string
    	price int
    }
    func main() {
    	o := order{
    		orderId: "1223",
    		price: 99,
    	}
    	//fmt.Println(o)
    	fmt.Println(createSql(o))
    
    }
    // 写一个函数,传入结构体,组装成如下sql :insert into order values(1234, 567)
    func createSql(o order ) string {
    	return fmt.Sprintf("insert into order values(%s, %d)",o.orderId,o.price)
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    3.2 进阶操作

    例如我们传入:

    e := employee {
           name: "lqz",
           id: 565,
           address: "上海",
           salary: 90000,
           country: "中国",
       }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    该函数返回

    insert into employee values("lqz", 565, "上海", 90000, "中国")
    
    • 1

    createSql函数应该适用于所有的结构体。因此,要编写这个函数,就必须在运行时检查传递过来的结构体参数的类型,找到结构体字段,接着创建查询。这时就需要用到反射了

    4. reflect 包之reflect.TypeOf()

    在 Go 语言中,reflect实现了运行时反射。reflect 包会帮助识别 interface{}变量的底层具体类型和具体值。这正是我们所需要的。createSql 函数接收 interface{} 参数,根据它的具体类型和具体值,创建 SQL 查询。这正是 reflect 包能够帮助我们的地方。

    任何接口值都由是一个具体类型具体类型的值两部分组成的

    在Go语言中反射的相关功能由内置的reflect包提供,任意接口值在反射中都可以理解为由reflect.Typereflect.Value两部分组成,并且reflect包提供了reflect.TypeOfreflect.ValueOf两个函数来获取任意对象的Value和Type

    4.1 reflect.Type 和 reflect.Value

    reflect.Type 表示 interface{} 的具体类型,而 reflect.Value 表示它的具体值。reflect.TypeOf()reflect.ValueOf() 两个函数可以分别返回 reflect.Typereflect.Value

    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    type order struct {
    	orderId string
    	price   int
    }
    
    func main() {
    	o := order{
    		orderId: "1223",
    		price:   99,
    	}
    	createSql(o)
    
    }
    func createSql(o interface{}) {
      // t为reflect.Type类型,v为reflect.Value类型
    	//var t reflect.Type = reflect.TypeOf(o)
    	//var v reflect.Value = reflect.ValueOf(o)
    	t := reflect.TypeOf(o)
    	v := reflect.ValueOf(o)
    	fmt.Println(t)
    	fmt.Println(v)
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    4.2 relfect.Kind

    在反射中关于类型还划分为两种:类型(Type)和种类(Kind)
    因为在Go语言中我们可以使用type关键字构造很多自定义类型,而种类(Kind)就是指底层的类型,但在反射中,当需要区分指针、结构体等大品种的类型时,就会用到种类(Kind)

    import (
    	"fmt"
    	"reflect"
    )
    
    type order struct {
    	orderId string
    	price   int
    }
    
    func main() {
    	o := order{
    		orderId: "1223",
    		price:   99,
    	}
    	createSql(o)
    
    }
    func createSql(o interface{}) {
    	t := reflect.TypeOf(o)
    	//reflect.Type 类型的方法
    	//var k reflect.Kind=t.Kind()
    	k:=t.Kind()
    	fmt.Println(k)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    Type 表示 interface{} 的实际类型(在这里是 main.Order),而 Kind 表示该类型的特定类别(在这里是 struct

    var i int =10
    var p *int =&i
    var s string="lqz is nb"
    var sl []int=[]int{7,8,9}
    fmt.Println(reflect.TypeOf(i))
    fmt.Println(reflect.TypeOf(i).Kind())
    fmt.Println("-----------")
    fmt.Println(reflect.TypeOf(p))
    fmt.Println(reflect.TypeOf(p).Kind())
    fmt.Println("-----------")
    fmt.Println(reflect.TypeOf(s))
    fmt.Println(reflect.TypeOf(s).Kind())
    fmt.Println("-----------")
    fmt.Println(reflect.TypeOf(sl))
    fmt.Println(reflect.TypeOf(sl).Kind())
    fmt.Println("-----------")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    reflect包中定义的Kind类型如下:

    const (
        Invalid Kind = iota  // 非法类型
        Bool                 // 布尔型
        Int                  // 有符号整型
        Int8                 // 有符号8位整型
        Int16                // 有符号16位整型
        Int32                // 有符号32位整型
        Int64                // 有符号64位整型
        Uint                 // 无符号整型
        Uint8                // 无符号8位整型
        Uint16               // 无符号16位整型
        Uint32               // 无符号32位整型
        Uint64               // 无符号64位整型
        Uintptr              // 指针
        Float32              // 单精度浮点数
        Float64              // 双精度浮点数
        Complex64            // 64位复数类型
        Complex128           // 128位复数类型
        Array                // 数组
        Chan                 // 通道
        Func                 // 函数
        Interface            // 接口
        Map                  // 映射
        Ptr                  // 指针
        Slice                // 切片
        String               // 字符串
        Struct               // 结构体
        UnsafePointer        // 底层指针
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    5. reflect 包之reflect.ValueOf()

    5.1 reflect.ValueOf

    reflect.ValueOf()返回的是reflect.Value类型,其中包含了原始值的值信息。reflect.Value与原始值之间可以互相转换。

    reflect.Value类型提供的获取原始值的方法如下:

    方法说明
    Interface() interface {}将值以 interface{} 类型返回,可以通过类型断言转换为指定类型
    Int() int64将值以 int 类型返回,所有有符号整型均可以此方式返回
    Uint() uint64将值以 uint 类型返回,所有无符号整型均可以此方式返回
    Float() float64将值以双精度(float64)类型返回,所有浮点数(float32、float64)均可以此方式返回
    Bool() bool将值以 bool 类型返回
    Bytes() []bytes将值以字节数组 []bytes 类型返回
    String() string将值以字符串类型返回
    var i int =10
    var v reflect.Value=reflect.ValueOf(i)
    //v:=reflect.ValueOf(i)
    fmt.Println(v)
    
    • 1
    • 2
    • 3
    • 4

    5.2 通过反射获取真正的值(v.Int(),v.String(),v.Interface())

    // int 类型
    //var i int =10
    //var v reflect.Value=reflect.ValueOf(i)
    //var res int64=v.Int()
    //fmt.Println(res)
    
    // string 类型
    //var s string ="lqz is nb"
    //var v reflect.Value=reflect.ValueOf(s)
    //var res string=v.String()
    //fmt.Println(res)
    
    // 结构体类型
    var o order=order{"1234",99}
    v:=reflect.ValueOf(o)
    var res interface{}=v.Interface()
    fmt.Println(res)
    // 通过类型断言,得到真正的类型具体值
    var o1 order=res.(order)
    fmt.Println(o1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    v.Kind()与reflect.Int64,reflect.Float32,reflect.Struct相关比较

    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    type order struct {
    	orderId string
    	price   int
    }
    
    func getRealValue(i interface{})  {
    	v := reflect.ValueOf(i)
    	k := v.Kind()
    	switch k {
    	case reflect.Int64:
    		// v.Int()从反射中获取整型的原始值,然后通过int64()强制类型转换
    		fmt.Println("该类型是空Int64类型,值为", int64(v.Int()))
    	case reflect.Float32:
    		// v.Float()从反射中获取浮点型的原始值,然后通过float32()强制类型转换
    		fmt.Println("该类型是空Float32类型,值为", float32(v.Float()))
    	case reflect.Float64:
    		// v.Float()从反射中获取浮点型的原始值,然后通过float64()强制类型转换
    		fmt.Println("该类型是空Float64类型,值为", float64(v.Float()))
    	case reflect.Struct:
    		fmt.Println("该类型是结构体类型,值为",v.Interface())
    		// 转成空接口类型,再断言成对应类型
    		i:=v.Interface()
    		var o order = i.(order)
    		fmt.Println(o)
    
    	}
    }
    func main() {
    	var i int64 =10
    	var b float32=9.9
    	var c float64=8.88
    	var o order=order{"1223",99}
    	getRealValue(i)
    	getRealValue(b)
    	getRealValue(c)
    	getRealValue(o)
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    5.3 通过反射设置变量的值(Elem())

    想要在函数中通过反射修改变量的值,需要注意函数参数传递的是值拷贝,必须传递变量地址才能修改变量值。而反射中使用专有的Elem()方法来获取指针对应的值

    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    func main() {
    	// int64 的修改
    	var i int64=100
    	// 错误做法:由于把函数传参是copy传递,把i传入,无法修改原来真正的值
    	//v1:=reflect.ValueOf(i)
    	//if v1.Kind()==reflect.Int64{
    	//	v1.SetInt(88)  // 报错
    	//}
    	// 正确做法
    	v2:=reflect.ValueOf(&i)
    	if v2.Kind()==reflect.Ptr{
    		v2.Elem().SetInt(88)
    	}
    	fmt.Println(i)
    
    
    	// 字符串的修改
    	//var s string = "lqz is nb"
    	// 由于把函数传参是copy传递,把s传入,无法修改原来真正的值
    	//v1:=reflect.ValueOf(s)
    	//if v1.Kind() == reflect.String {
    	//	v1.SetString("xxxx") // 报错
    	//}
    
    	// 把指针传入,可以修改原来的值
    	//v2 := reflect.ValueOf(&s)
    	//if v2.Kind() == reflect.Ptr {
    	//	v2.Elem().SetString("xxxx")
    	//}
    	//fmt.Println(s)
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    5.4 isNil()和isValid()

    isNil()

    func (v Value) IsNil() bool
    
    • 1

    IsNil()报告v持有的值是否为nil。v持有的值的分类必须是通道、函数、接口、映射、指针、切片之一;否则IsNil函数会导致panic。

    isValid()

    func (v Value) IsValid() bool
    
    • 1

    IsValid()返回v是否持有一个值。如果v是Value零值会返回假,此时v除了IsValid、String、Kind之外的方法都会导致panic。

    IsNil()常被用于判断指针是否为空;IsValid()常被用于判定返回值是否有效。

    	//1  IsNil()
    	//var a *int
    	//v := reflect.ValueOf(a)
    	//var res1 bool = v.IsNil()
    	//fmt.Println(res1) // true
    
    	//2  IsValid
    	var m map[int]string= map[int]string{1:"lqz",2:"zs"}
    
    	//v:=reflect.ValueOf(m)
    	//fmt.Println(v.IsValid()) //m的值是有效的
    
    	res:=reflect.ValueOf(m).MapIndex(reflect.ValueOf(1)).IsValid()
    	fmt.Println(res)  // true
    	res1:=reflect.ValueOf(m).MapIndex(reflect.ValueOf(9)).IsValid()
    	fmt.Println(res1)  // false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    6. 反射结构体

    任意值通过reflect.TypeOf()获得反射对象信息后,如果它的类型是结构体,可以通过反射值对象(reflect.Type)的NumField()Field()方法获得结构体成员的详细信息

    reflect.Type中与获取结构体成员相关的的方法如下表所示。

    方法说明
    Field(i int) StructField根据索引,返回索引对应的结构体字段的信息。
    NumField() int返回结构体成员字段数量。
    FieldByName(name string) (StructField, bool)根据给定字符串返回字符串对应的结构体字段的信息。
    FieldByIndex(index []int) StructField多层成员访问时,根据 []int 提供的每个结构体的字段索引,返回字段的信息。
    FieldByNameFunc(match func(string) bool) (StructField,bool)根据传入的匹配函数匹配需要的字段。
    NumMethod() int返回该类型的方法集中方法的数目
    Method(int) Method返回该类型方法集中的第i个方法
    MethodByName(string)(Method, bool)根据方法名返回该类型方法集中的方法

    6.1 反射结构体字段

    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    type Order struct {
    	orderId string `json:"order_id"`
    	price   int `json:"price"`
    }
    
    func main() {
    	var o Order = Order{"1234",99}
    	t := reflect.TypeOf(o)
    	fmt.Println(t.Name(), t.Kind()) // Order    struct
    	fmt.Println(t.NumField())  // 结构体中有俩个字段
    	// 通过for循环遍历结构体的所有字段信息
    	for i := 0; i < t.NumField(); i++ {
    		field := t.Field(i)
    		fmt.Printf("name:%s index:%d type:%v json tag:%v\n", field.Name, field.Index, field.Type, field.Tag.Get("json"))
    	}
    	// 通过字段名获取指定结构体字段信息
    	if priceField, ok := t.FieldByName("price"); ok {
    		fmt.Printf("name:%s index:%d type:%v json tag:%v\n", priceField.Name, priceField.Index, priceField.Type, priceField.Tag.Get("json"))
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    6.2 反射结构体方法并调用

    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    type Order struct {
    	orderId string `json:"order_id"`
    	price   int `json:"price"`
    }
    
    func (o Order)PrintOrder()  {
    	fmt.Printf("订单号为:%s,价格为:%d\n",o.orderId,o.price)
    }
    func (o Order)ChangePrice(price int)  {
    	o.price=price
    	fmt.Println("价格被改为:",o.price)
    }
    func main() {
    	var o Order = Order{"1234",99}
    	t := reflect.TypeOf(o)
    	v:=reflect.ValueOf(o)
    	fmt.Println(v.NumMethod())  // 结构体中有俩个方法
    	for i := 0; i < v.NumMethod(); i++ {
    		methodType := v.Method(i).Type()
    		fmt.Printf("方法名::%s\n", t.Method(i).Name)
    		fmt.Printf("方法是类型是:%s\n", methodType)
    	}
    	// 通过MethodByName 获取函数,调用PrintOrder
    	res:=v.MethodByName("PrintOrder")
    	// 通过反射调用方法传递的参数必须是 []reflect.Value 类型
    	var args1 = []reflect.Value{}
    	res.Call(args1)
    	fmt.Println("-------")
    	// 通过MethodByName 获取函数,调用PrintOrder
    	res1:=v.MethodByName("ChangePrice")
    	// 通过反射调用方法传递的参数必须是 []reflect.Value 类型
    	var args2 = []reflect.Value{reflect.ValueOf(99)}
    	res1.Call(args2)
    	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    6.3 修改结构体字段

    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    type Order struct {
    	OrderId string `json:"order_id"`
    	price   int `json:"price"`
    }
    
    func (o Order)PrintOrder()  {
    	fmt.Printf("订单号为:%s,价格为:%d\n",o.OrderId,o.price)
    }
    func (o Order)ChangePrice(price int)  {
    	o.price=price
    	fmt.Println("价格被改为:",o.price)
    }
    func main() {
    	var o Order = Order{"1234",99}
    	v:=reflect.ValueOf(&o)
    	v = v.Elem()
    	// 取字段
    	f := v.FieldByName("OrderId")
    	if f.Kind() == reflect.String {
    		f.SetString("新id")
    	}
    	fmt.Println(o)
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    7. 通过反射实现拼接sql

    package main
    import (
    	"fmt"
    	"reflect"
    )
    
    type order struct {
    	ordId      int
    	customerId int
    }
    
    type employee struct {
    	name    string
    	id      int
    	address string
    	salary  int
    	country string
    }
    
    func createQuery(q interface{}) {
    	if reflect.ValueOf(q).Kind() == reflect.Struct {
    		t := reflect.TypeOf(q).Name() // 结构体名字
    		query := fmt.Sprintf("insert into %s values(", t)  // 先拼接成insert into 结构体名字 values(
    		v := reflect.ValueOf(q)
    		for i := 0; i < v.NumField(); i++ {
    			switch v.Field(i).Kind() {
    			case reflect.Int:
    				if i == 0 {
    					query = fmt.Sprintf("%s%d", query, v.Field(i).Int())
    				} else {
    					query = fmt.Sprintf("%s, %d", query, v.Field(i).Int())
    				}
    			case reflect.String:
    				if i == 0 {
    					query = fmt.Sprintf("%s\"%s\"", query, v.Field(i).String())
    				} else {
    					query = fmt.Sprintf("%s, \"%s\"", query, v.Field(i).String())
    				}
    			default:
    				fmt.Println("Unsupported type")
    				return
    			}
    		}
    		query = fmt.Sprintf("%s)", query)
    		fmt.Println(query)
    		return
    
    	}
    	fmt.Println("类型不支持")
    }
    
    func main() {
    	o := order{
    		ordId:      456,
    		customerId: 56,
    	}
    	createQuery(o)
    
    	e := employee{
    		name:    "lqz",
    		id:      565,
    		address: "上海",
    		salary:  90000,
    		country: "中国",
    	}
    	createQuery(e)
    	i := 90
    	createQuery(i)
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
  • 相关阅读:
    MyBatis多条件查询、动态SQL、多表操作、注解开发详细教程
    ScanNet数据集转rosbag的脚本
    照片怎么压缩变小?
    Linux centos7安装部署KETTLE-9.3.0
    【图像处理】基于双目视觉的物体体积测量算法研究(Matlab代码实现)
    微服务项目:尚融宝(43)(核心业务流程:借款额度审批(3))
    10分钟极速入门dash应用开发
    【SpringBoot项目】一文掌握文件上传和下载【业务开发day04】
    工具应用:使用JMeter实现Phpwind的性能测试!
    《设计模式》之迭代器模式
  • 原文地址:https://blog.csdn.net/qq_55752792/article/details/126127387