我们先看看什么是反射,它有什么用。
我们先看卡wiki上关于反射的介绍。 链接为https://en.wikipedia.org/wiki/Reflection_(computer_programming)
In computer science, reflection is the ability of a process to examine, introspect, and modify its own structure and behavior.[1]
反射的主要作用:Reflection helps programmers make generic software libraries to display data, process different formats of data, perform serialization or deserialization of data for communication, or do bundling and unbundling of data for containers or bursts of communication.
通俗的讲, 反射是指在程序运行期对程序数据结构和行为本身进行访问和修改的能力。
支持反射的语言可以在程序编译期将变量的反射信息,如字段名称、类型信息、结构体信息等整合到可执行文件中,并给程序提供接口访问反射信息,这样就可以在程序运行期获取类型的反射信息,并且有能力修改它们。
Go语言与其他高级语言一样,都有反射功能。
Go 反射包
reflect包有两个数据类型是最重要的,一个是Type,一个是Value。
Type就是定义的类型的一个数据类型,Value是值的类型。具体的Type和Value里面包含的方法参看文档:https://pkg.go.dev/reflect?tab=doc
简单示例
说明,本示例介绍如何通过反射访问包,包的字段属性类型,字段属性值,以及如何修改属性值。 本示例没有介绍如何访问方法以及动态调用方法。
package main
import (
"fmt"
"reflect"
)
type DeviceStatus uint8
const (
Offline DeviceStatus = 0
Online DeviceStatus = 1
)
type Category struct {
ID int32
Name string `json:"name" value:"空调"`
Description string
}
func main() {
var category Category
var i int32 = -5
// 类型(Type)指的是系统原生数据类型,如 int、string、bool、float32 等类型,以及使用 type 关键字定义的类型
// 种类(Kind)指的是对象归属的品种. 在go语言中是固定定义好的,可以通过查看Kind定义知道全部Kind
typeInfo, kindInfo := reflectTypeAndKind(category)
fmt.Printf("struct type reflect info. name:%s, kind:%s \n", typeInfo, kindInfo)
enumerateFieldByReflect(category)
typeOfCagegory := reflect.TypeOf(category)
typeInfo, kindInfo = reflectTypeAndKind(typeOfCagegory)
fmt.Printf("type reflect info of reflect. name:%s, kind:%s \n", typeInfo, kindInfo)
enumerateFieldByReflect(category)
fmt.Println("categroy is not initialized.")
getAndUpdateFieldValueByReflect(&category)
category = Category{ID:001, Name: "空调", Description: "空调大类"}
fmt.Printf("---updateFieldValue by reflect. rawValue:%v \n\n", category)
fmt.Println("categroy is initialized.")
fmt.Printf("---enumerateFieldValue by reflect. rawValue:%v \n", category)
enumerateFieldByReflect(category)
fmt.Printf("---start of updateFieldValue by reflect. rawValue:%v \n", category)
getAndUpdateFieldValueByReflect(&category)
fmt.Printf("---end of updateFieldValue by reflace. newValue:%v \n\n", category)
typeInfo, kindInfo = reflectTypeAndKind(i)
fmt.Printf("primitive type reflect info. name:%s, kind:%v \n", typeInfo, kindInfo)
enumerateFieldByReflect(i)
getAndUpdateFieldValueByReflect(i)
myFunc := reflectTypeAndKind
typeInfo, kindInfo = reflectTypeAndKind(myFunc)
fmt.Printf("func type reflect info. name:%s, kind:%v \n", typeInfo, kindInfo)
enumerateFieldByReflect(myFunc)
getAndUpdateFieldValueByReflect(myFunc)
var devStatus DeviceStatus = Online
typeInfo, kindInfo = reflectTypeAndKind(devStatus)
fmt.Printf("enum type reflect info. name:%s, kind:%v \n", typeInfo, kindInfo)
enumerateFieldByReflect(devStatus)
getAndUpdateFieldValueByReflect(devStatus)
}
func reflectTypeAndKind(x interface{}) (typeInfo string, kindInfo reflect.Kind) {
reflectObject := reflect.TypeOf(x)
return reflectObject.Name(), reflectObject.Kind()
}
func enumerateFieldByReflect(x interface{}) {
reflectObject := reflect.TypeOf(x)
reflectValue := reflect.ValueOf(x)
// NumField returns a struct type's field count.
// It panics if the type's Kind is not Struct.
if reflectObject.Kind() == reflect.Struct {
num := reflectObject.NumField();
fmt.Printf("there are %d fields\n", num)
for i := 0; i < num; i++ {
// 获取每个属性的结构体字段类型
fieldType := reflectObject.Field(i)
filedValue := reflectValue.Field(i)
fieldKind := filedValue.Kind()
var rawIntValue int32
var rawStrValue string
switch fieldKind {
case reflect.Int32:
//获取64位的值,强制类型转换为int类型
rawIntValue = filedValue.Interface().(int32)
case reflect.String:
rawStrValue = filedValue.Interface().(string)
default:
}
// 输出属性名和tag
fmt.Printf("%dth, name: %v, type %v, tag: '%v', currentIntValue:%v, currentStrValue:%v \n",
i, fieldType.Name, fieldType.Type, fieldType.Tag, rawIntValue, rawStrValue)
}
// 通过字段名, 找到字段类型信息
if nameField, ok := reflectObject.FieldByName("Name"); ok {
// 从tag中取出需要的tag
fmt.Println(nameField.Tag.Get("json"), nameField.Tag.Get("value"))
} else {
fmt.Println("no name filed")
}
// It panics if v's Kind is not struct.
fmt.Println("不存在的结构体成员:", reflect.ValueOf(x).FieldByName("").IsValid())
} else {
fmt.Println("non-struct, no filed")
}
}
/*
* 更新值,必须传入指针类型, 反射只能修改可以导出的属性(也就是大写字母开始的属性)
*/
func getAndUpdateFieldValueByReflect(x interface{}) {
reflectObject := reflect.TypeOf(x)
reflectValue := reflect.ValueOf(x)
reflectKind := reflectObject.Kind()
actualReflectKind := reflect.Ptr
if reflectKind == reflect.Ptr {
actualReflectKind = reflect.Struct
fmt.Printf("ptr type: %T\n", x)
reflectObject = reflectObject.Elem()
actualReflectKind = reflectObject.Kind()
reflectValue = reflectValue.Elem()
}
// NumField returns a struct type's field count.
// It panics if the type's Kind is not Struct.
if actualReflectKind == reflect.Struct {
num := reflectObject.NumField();
fmt.Printf("there are %d fields, reflectKind:%v, actualReflectKind:%v\n",
num, reflectKind, actualReflectKind)
for i := 0; i < num; i++ {
// 获取每个属性的结构体字段类型
fieldType := reflectObject.Field(i)
filedValue := reflectValue.Field(i)
fieldKind := filedValue.Kind()
// 输出属性名和tag
fmt.Printf("%dth, name: %v, type %v, tag: '%v', filedValue:%v, fieldValueInterface:%v \n",
i, fieldType.Name, fieldType.Type, fieldType.Tag, filedValue, filedValue.Interface())
switch fieldKind {
case reflect.Int32:
newIntValue := 999 + filedValue.Interface().(int32)
// 强制将int32转换为int64,否则无法传入参数
filedValue.SetInt(int64(newIntValue))
case reflect.String:
newStrValue := "aaa-" + filedValue.Interface().(string)
filedValue.SetString(newStrValue)
default:
}
}
// 通过字段名, 找到字段类型信息
if nameField, ok := reflectObject.FieldByName("Name"); ok {
fmt.Println(nameField.Tag.Get("json"), nameField.Tag.Get("value"), reflectValue.FieldByName("Name"))
} else {
fmt.Println("no name filed")
}
} else {
fmt.Printf("non-struct, no filed. reflectKind:%v, rawValue: %v \n", reflectKind, reflectValue)
}
}
运行结果:
```bash
C:\F\yqgopath\src\github.com\mygototurials\reflectdemo>go run main.go
struct type reflect info. name:Category, kind:struct
there are 3 fields
0th, name: ID, type int32, tag: '', currentIntValue:0, currentStrValue:
1th, name: Name, type string, tag: 'json:"name" value:"空调"', currentIntValue:0, currentStrValue:
2th, name: Description, type string, tag: '', currentIntValue:0, currentStrValue:
name 空调
不存在的结构体成员: false
type reflect info of reflect. name:, kind:ptr
there are 3 fields
0th, name: ID, type int32, tag: '', currentIntValue:0, currentStrValue:
1th, name: Name, type string, tag: 'json:"name" value:"空调"', currentIntValue:0, currentStrValue:
2th, name: Description, type string, tag: '', currentIntValue:0, currentStrValue:
name 空调
不存在的结构体成员: false
categroy is not initialized.
ptr type: *main.Category
there are 3 fields, reflectKind:ptr, actualReflectKind:struct
0th, name: ID, type int32, tag: '', filedValue:0, fieldValueInterface:0
1th, name: Name, type string, tag: 'json:"name" value:"空调"', filedValue:, fieldValueInterface:
2th, name: Description, type string, tag: '', filedValue:, fieldValueInterface:
name 空调 aaa-
---updateFieldValue by reflect. rawValue:{1 空调 空调大类}
categroy is initialized.
---enumerateFieldValue by reflect. rawValue:{1 空调 空调大类}
there are 3 fields
0th, name: ID, type int32, tag: '', currentIntValue:1, currentStrValue:
1th, name: Name, type string, tag: 'json:"name" value:"空调"', currentIntValue:0, currentStrValue:空调
2th, name: Description, type string, tag: '', currentIntValue:0, currentStrValue:空调大类
name 空调
不存在的结构体成员: false
---start of updateFieldValue by reflect. rawValue:{1 空调 空调大类}
ptr type: *main.Category
there are 3 fields, reflectKind:ptr, actualReflectKind:struct
0th, name: ID, type int32, tag: '', filedValue:1, fieldValueInterface:1
1th, name: Name, type string, tag: 'json:"name" value:"空调"', filedValue:空调, fieldValueInterface:空调
2th, name: Description, type string, tag: '', filedValue:空调大类, fieldValueInterface:空调大类
name 空调 aaa-空调
---end of updateFieldValue by reflace. newValue:{1000 aaa-空调 aaa-空调大类}
primitive type reflect info. name:int32, kind:int32
non-struct, no filed
non-struct, no filed. reflectKind:int32, rawValue: -5
func type reflect info. name:, kind:func
non-struct, no filed
non-struct, no filed. reflectKind:func, rawValue: 0x49e950
enum type reflect info. name:DeviceStatus, kind:uint8
non-struct, no filed
non-struct, no filed. reflectKind:uint8, rawValue: 1
C:\F\yqgopath\src\github.com\mygototurials\reflectdemo>
对代码的解释
1, 使用 reflect.TypeOf() 函数可以获得任意类型的类型对象(reflect.Type),程序通过类型对象可以访问任意类型的类型信息。下面通过例子来理解获取类型对象的过程:
TypeOf方法额返回值为Type
// TypeOf returns the reflection Type that represents the dynamic type of i.
// If i is a nil interface value, TypeOf returns nil.
func TypeOf(i interface{}) Type {
eface := *(*emptyInterface)(unsafe.Pointer(&i))
return toType(eface.typ)
}
type.Kind() 直接返回具体的int或者string之类的, 我们可以看Kind的定义:
// A Kind represents the specific kind of type that a Type represents.
// The zero Kind is not a valid kind.
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
)
2, 使用 reflect.ValueOf() 函数可以获得任意值的类型对象(reflect.Value),程序通过值类型对象可以访问任意值的类型信息和值当前内容。下面通过例子来理解获取类型对象的过程:
type方法额返回值为Type
// ValueOf returns a new Value initialized to the concrete value
// stored in the interface i. ValueOf(nil) returns the zero Value.
func ValueOf(i interface{}) Value {
if i == nil {
return Value{}
}
// TODO: Maybe allow contents of a Value to live on the stack.
// For now we make the contents always escape to the heap. It
// makes life easier in a few places (see chanrecv/mapassign
// comment below).
escapes(i)
return unpackEface(i)
}
3, 通过反射获取指针指向的元素类型:reflect.Elem()。 例如我们传递44行, getAndUpdateFieldValueByReflect(&category)
那么就可以通过reflectObject = reflectObject.Elem()得到真实的元素类型
reflectObject := reflect.TypeOf(x)
reflectValue := reflect.ValueOf(x)
reflectKind := reflectObject.Kind()
actualReflectKind := reflect.Ptr
if reflectKind == reflect.Ptr {
actualReflectKind = reflect.Struct
fmt.Printf("ptr type: %T\n", x)
reflectObject = reflectObject.Elem()
actualReflectKind = reflectObject.Kind()
reflectValue = reflectValue.Elem()
}