反射
反射(Reflection):在编译不确定对象的数据类型的前提下,操作对象的机制
1)对象在编译器期间会被转换为内存地址,不会被写入可执行部分;
2)Go语言中的反射功能由reflect包提供;
reflect
reflect包:Go语言中提供反射功能的包
1)reflect包中提供两种自定义数据类型:Type(接口)、Value(结构体)
2)Type和Value分别对应多种方法可操作指定对象;
reflect.TypeOf()函数:返回反射对象(Type类型)代表指定对象数据类型
1)调用格式:reflect.TypeOf(对象)
2)其形参数数据类型为“interface{}”(可接收任意数据类型的对象);
3)reflect包中重新定义数据类型(调用格式:reflect.数据类型),如下:
type Kind uint
const (
Invalid Kind = iota
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr
Float32
Float64
Complex64
Complex128
Array
Chan
Func
Interface
Map
Ptr
Slice
String
Struct
UnsafePointer
)
reflect.ValueOf()函数:返回指定对象的值(未定义,则返回nil)
1)调用格式:reflcet.ValueOf(对象)
2)返回值的数据类型(Value类型)不同于原数据类型;
3)其形参数数据类型为“interface{}
”(可接收任意数据类型的对象);
如:通过reflect.TypeOf()和reflect.ValueOf()函数输出对象的数据类型和值
1)编写程序;
package main
import (
"fmt"
"reflect"
)
func main() {
v := 3
num2 := reflect.TypeOf(v)
fmt.Println(num2)
fmt.Println(num2.String()) //string()方法也可输出其数据类型
num1 := reflect.ValueOf(v)
fmt.Println(num1)
}
2)运行结果;
其他常用函数:
函数名 | 说明 |
---|---|
reflect.PtrTo(反射对象) | 返回该反射对象代表类型的 指针类型形式 (返回值类型为Type) |
reflect.SliceOf(反射对象) | 返回该反射对象代表类型的 切片类型形式 (返回值类型为Type) |
reflect.MapOf(反射对象1,反射对象2) | 返回以反射对象1和2代表 键和值的map类型形式 (返回值类型为Type) |
如:通过上述函数返回整数类型的其他类型形式
1)编写程序;
package main
import (
"fmt"
"reflect"
)
func main() {
var num int = 3
NumType := reflect.TypeOf(num)
NumPtr := reflect.PtrTo(NumType)
NumSlice := reflect.SliceOf(NumType)
NumMap := reflect.MapOf(NumPtr, NumSlice)
fmt.Println(num, NumType, NumPtr, NumSlice, NumMap)
}
2)运行结果;
Type
Type(接口数据类型):reflect包中Type接口定义较多函数/方法以操作对象
1)Type接口中定义方法的原型如下:返回该反射对象的自定义数据类型
type Type interface {
Kind() Kind // 返回该反射对象底层数据类型
Name() string // 返回该反射对象的自定义数据类型
PkgPath() string // 返回该反射对象代表类型所在的包路径
String() string // 以字符串形式返回底层数据类型
// 若比较两个类型是否相等,用Type类型比较
Size() uintptr // 返回要保存一个该类型的值需要多少字节
Align() int // 返回当从内存中申请一个该类型值时,会对齐的字节数
FieldAlign() int // 返回当该类型作为结构体的字段时,会对齐的字节数
Implements(u Type) bool // 若该类型实现了u代表的接口,会返回真
AssignableTo(u Type) bool // 若该类型的值可以赋值给u代表的类型,返回真
ConvertibleTo(u Type) bool // 若该类型的值可以转换为u代表的类型,返回真
// 返回该类型的字位数
Bits() int // 返回代表内置类型的字位数
Len() int // 返回代表数组类型的长度
Elem() Type // 返回代表数据集合的元素类型
Key() Type // 返回代表map类型的键类型
ChanDir() ChanDir // 返回代表通道类型的方向
NumField() int // 返回代表结构体类型含有的字段数(包括匿名字段)
Field(i int) StructField // 返回代表结构体类型的第i个字段
FieldByIndex(index []int) StructField // 通过索引指定的嵌套字段类型
FieldByName(name string) (StructField, bool) // 返回名为name的字段
// 会同时匹配匿名字段和子字段,bool类型代表是否匹配成功
FieldByNameFunc(match func(string) bool) (StructField, bool)
// 返回第一个字段名满足函数match的字段,bool类型代表是否匹配成功
IsVariadic() bool // 判断函数的最后一个参数是否为“...”形式
NumIn() int // 返回代表函数类型所包含的参数个数
In(i int) Type // 返回代表函数类型的第i个参数
NumOut() int // 返回代笔函数类型的返回值个数
Out(i int) Type // 返回代表函数类型的第i个返回值
NumMethod() int // 返回反射对象所包含的方法数(包括匿名字段所带的)
Method(int) Method // 返回反射对象包含方法中的第i个方法
// 所包含的方法安装第一个字母的ASCII码值顺序排列
MethodByName(string) (Method, bool) // 返回名为string的方法,bool类型代表是否匹配成功
}
普通数据类型的常用方法如下:
方法名 | 说明 |
---|---|
Kind() | 返回该反射对象的底层数据类型 |
Name() | 返回该反射对象的自定义数据类型 (无,则返回空字符串) |
Elem() | 返回指针指向对象 (指针的数据类型均为ptr) |
//常使用Elem()方法间接修改值传递的反射对象
如:通过上述方法分别判断对象的数据类型
1)编写程序;
package main
import (
"fmt"
"reflect"
)
type Myint int
type Mycat struct{}
func main() {
var num Myint = 0
NumType := reflect.TypeOf(num)
fmt.Println(NumType.Name(), NumType.Kind())
cat := Mycat{}
CatType := reflect.TypeOf(cat)
fmt.Println(CatType.Name(), CatType.Kind())
CatPtr := &cat
PtrType := reflect.TypeOf(CatPtr)
fmt.Println(PtrType.Name(), PtrType.Kind(), PtrType.Elem())
}
2)运行结果;
结构体类型关于字段的常用方法如下(非结构体类型调用会产生宕机):
方法名 | 说明 |
---|---|
Field(N) | 返回该结构体的第N个字段 (以StructField结构体的形式返回) |
NumField() | 返回该结构体的字段数量 |
FieldByName(字符串) | 检测结构体是否包含指定字符串的字段 |
StructField自定义的结构体类型所含字段如下:
type StructField struct {
Name string // 字段名
PkgPath string // 非导出字段的包路径
Type Type // 字段反射类型
Tag StructTag // 字段标签
Offset uintptr // 字段在结构体中相对偏移
Index []int // Type.FieldByIndex中返回的索引值
Anonymous bool // 是否为匿名字段
}
如:通过上述方法获取结构体的数据信息
1)编写程序;
package main
import (
"fmt"
"reflect"
)
type Mycat struct {
Name string
Type int `json:"type" id:"100"` //带有结构体tag的字段
}
func main() {
cat := Mycat{Name: "mimi", Type: 1}
CatType := reflect.TypeOf(cat)
for i := 0; i < CatType.NumField(); i++ {
fieldType := CatType.Field(i)
fmt.Println(fieldType.Name, fieldType.Tag)
}
if whatType, ok := CatType.FieldByName("Type"); ok {
fmt.Println(whatType.Tag.Get("json"), whatType.Tag.Get("id"))
} //Tag字段的Get()方法可指定键以获得值
}
2)运行结果;
结构体类型关于方法的常用方法如下(非结构体类型调用会产生宕机):
方法名 | 说明 |
---|---|
NumMethod() | 返回包含的方法总数 |
Method(N) | 返回第N个方法 (以Method结构体的形式返回) |
MethodByName(字符串) | 检测是否包含指定方法 |
Method自定义结构体类型所含字段如下:
type Method struct {
Name string // 方法名
PkgPath string // 非导出字段的包路径
Type Type // 方法类型
Func Value // 方法值
Index int // Type.Method的索引
}
如:通过反射对象获取对象的方法信息
1)编写程序;
package main
import (
"fmt"
"reflect"
)
type Animal struct {
Name string
Age int
}
func (a Animal) Cmeth() {}
func (a Animal) Bmeth() {}
func (a Animal) Ameth() {}
func main() {
cat := Animal{"mimi", 12}
CatType := reflect.TypeOf(cat)
for i := 0; i < CatType.NumMethod(); i++ {
fmt.Println(CatType.Method(i).Name)
if CatType.Method(i).Name == "Ameth" {
fmt.Println("This is A")
}
}
fmt.Println(cat)
}
2)运行结果;
Value
Value(结构体数据类型):reflect包中Value定义较多函数/方法以操作对象
1)Value结构体中定义的方法只能由对应的数据类型调用,否则会产生宕机;
2)Value结构体实现Type接口中定义的方法(接收者为Value);
3)Value结构体对应定义方法的原型如下:
func (v Value) CanInterface() bool // 是否可调用Interface()方法
func (v Value) Interface() (i interface{}) // 返回对象的反射值(可导出的)
func (v Value) IsNil() bool // 判断反射值是否为nil
func (v Value) Call(in []Value) []Value // 调用代表的函数/方法,参数需通过Value类型的Slice传入
func (v Value) Send(x Value) // 向代表通道发送数据(阻塞)
func (v Value) TrySend(x Value) bool // 尝试向代表通道发送数据x(无阻塞)
func (v Value) Recv() (x Value, ok bool) // 从代表通道接收数组(阻塞)
func (v Value) TryRcv()(x Value, ok bool) // 尝试从代表通道接收数据(无阻塞)
func (v Value) Close() // 关闭代表通道
func (v Value) CanSet() bool // 检测v是否可被修改
// 不可修改则返回false,且任何形式的Set方法调用都会panic
func (v Value) Set(x Value) // 将v的值改为其他Value类型的值为x
func (v Value) SetBool(x bool) // 修改bool类型的值为x
func (v Value) SetInt(x int64) // 修改整数类型的值为x
func (v Value) SetUint(x uint64) // 修改无符号整数类型的值为x
func (v Value) SetFloat(x float64) // 修改浮点数类型的值为x
func (v Value) SetComplex(x complex128) // 修改复数类型的值为x
func (v Value) SetBytes(x []byte) // 修改byte类型的值为x
func (v Value) SetString(x string) // 修改字符串类型的值为x
func (v Value) SetPointer(x unsafe.Pointer) // 修改指针类型的值为x
func (v Value) SetCap(n int) // 修改Slice的容量为n
func (v Value) SetLen(n int) // 修改Slice的长度为n
func (v Value) SetMapIndex(key, val Value) // 修改/添加map的键值对
常用函数定义原型如下:
1)将src中的元素复制到dst中, 返回复制成功个数(两者必须是数组或slice)
func Copy(dst, src Value) int
2)创建指向typ类型的指针(第二个参数传入地址)
func NewAt(typ Type, p unsfae.Pointer) Value
3)创建指定类型的对象,其值为零值
func Zero(typ Type) Value
4)创建指定长度和容量的Slice
func MakeSlice(typ Type, len, cap int) Value
5)创建键值类型均为typ的map
func MakeMap(typ Type) Value
6)创建函数
func MakeFUnc(typ Type, fn func(args []Value) (results []Value)) Value
7)创建元素类型为typ,buff个缓冲的通道
func MakeChan(typ Type, buffer int) Value
8) 向s切片末尾添加多个元素
func Append(s Value, x ...Value) Value
9) 向s切片末尾添加t元素
func AppendSLice(s, t Value) Value
10)判断任意两个对象的底层是否完全相等(常用于比较结构体成员和空接口类型变量)
func DeepEqual(a1, a2 interface{}) bool
如:通过反射值修改对象的字段
1)编写程序;
package main
import (
"fmt"
"reflect"
)
type Animal struct {
Name string
Age int
}
func (a Animal) Meth(name string) {
fmt.Println("Name Is:", name)
}
func main() {
cat := Animal{"mimi", 12}
CatValue := reflect.ValueOf(cat)
CatMeth := CatValue.MethodByName("Meth")
args := []reflect.Value{reflect.ValueOf("maomao")}
CatMeth.Call(args)
dog := &Animal{"wangwang", 10}
DogValue := reflect.ValueOf(dog)
if DogValue.Kind() != reflect.Ptr {
fmt.Println("必须为指针类型才可修改")
return
}
name := DogValue.Elem().FieldByName("Name")
if name.Kind() == reflect.String {
name.SetString("gogo")
}
fmt.Println(*dog)
}
2)运行结果;
//被修改对象和调用的函数/方法必须是可导出的