10-常用标准库之-reflect-反射

一 反射介绍

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

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

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

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

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

二 变量的内在机制

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

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

三 反射的实际用途

3.1 编写函数,它接收结构体作为参数,并用它来创建一个 SQL 插入查询

package main

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)

}

3.2 我们如何让他变得更通用

例如我们传入:

e := employee {
       name: "lqz",
       id: 565,
       address: "上海",
       salary: 90000,
       country: "中国",
   }

该函数返回

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

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

四 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)

}

4.2 relfect.Kind

在反射中关于类型还划分为两种:类型(Type)和种类(Kind)
因为在Go语言中我们可以使用type关键字构造很多自定义类型,而种类(Kind)就是指底层的类型,但在反射中,当需要区分指针、结构体等大品种的类型时,就会用到种类(Kind)
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.TypeOf(o)
	//reflect.Type 类型的方法
	//var k reflect.Kind=t.Kind()
	k:=t.Kind()
	fmt.Println(k)
}

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("-----------")

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

ype Kind uint
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        // 底层指针
)

五 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)

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)

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)

}

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)

}

5.4 isNil()和isValid()

isNil()

func (v Value) IsNil() bool

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

isValid()

func (v Value) IsValid() bool

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

六 反射结构体

任意值通过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"))
	}

}

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)
	
}

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)

}

七 通过反射实现拼接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)

}
posted @ 2022-03-12 01:39  刘清政  阅读(244)  评论(0编辑  收藏  举报