go语言中的反射Reflect初探

我们先看看什么是反射,它有什么用。
我们先看卡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()

	} 

你可能感兴趣的:(go)