目录


一、基本概念

在计算机科学领域,反射是指计算机程序在运行时(runtime)可以访问、检测和修改它本身状态或行为的一种能力。用比喻来说,反射就是程序在运行的时候能够“观察”并且修改自己的行为。

每种语言的反射模型都不同,并且有些语言根本不支持反射。Go 语言实现了反射,反射机制就是在运行时动态的调用对象的方法和属性,官方自带的 reflect 包就是反射相关的,只要包含这个包就可以使用。

二、数据结构

reflect 实现了运行时的反射能力,能够让程序操作不同类型的对象,其中有两对非常重要的函数和类型,分别是:

reflect.TypeOf 能获取类型信息;
reflect.ValueOf 能获取数据的运行时表示;

1、反射类型 Type

Go 程序中的类型(Type)指的是系统原生数据类型,如 int、string、bool、float32 等类型,以及使用 type 关键字定义的类型,这些类型的名称就是其类型本身的名称。

类型 reflect.Type 是反射包定义的一个接口,我们可以使用 reflect.TypeOf 函数获取任意变量的类型,reflect.Type 接口中定义了一些有趣的方法。

// Type 接口

type Type interface {

    // 对应着unsafe的align,是一个计算大小的方法
	Align() int

    // 字段大小,必须是type.kind=结构体类型
	FieldAlign() int

    // 第 i 个方法
	Method(int) Method  

    // 根据名称获取方法
	MethodByName(string) (Method, bool) 

    // 方法的个数
	NumMethod() int  

    // 获取结构体名称
	Name() string 

    // 包路径
	PkgPath() string 

    // 占用内存的大小
	Size() uintptr  

    // 获取字符串表述
	String() string  

    // 数据类型
	Kind() Kind

    // 判断是否实现了某接口
	Implements(u Type) bool 

    // 能否赋给另外一种类型
	AssignableTo(u Type) bool 

    // 能否转换为另外一种类型 
	ConvertibleTo(u Type) bool 

    // 解析指针(指针类型转为普通类型)
	Comparable() bool

    // 类型所占据的位数
	Bits() int
    
    // 返回通道的方向
	ChanDir() ChanDir

    // 返回类型是否是可变参数
	IsVariadic() bool

    // 返回内部子元素类型
	Elem() Type  

    // 第i个成员
	Field(i int) StructField 

    // 根据index路径获取嵌套成员
	FieldByIndex(index []int) StructField 

    // 根据名称获取成员
	FieldByName(name string) (StructField, bool)

    // 返回名称符合 func 函数的字段
	FieldByNameFunc(match func(string) bool) (StructField, bool)

    // 获取函数类型的第 i 个参数的类型
	In(i int) Type

    // 返回 map 的 key 类型
	Key() Type

    // 容器的长度
	Len() int 

    // 返回类型字段的数量
	NumField() int

    // 输出参数的个数
	NumIn() int

    // 返回参数的个数
	NumOut() int 

    // 返回函数类型的第 i 个值的类型
	Out(i int) Type

    // 返回类型结构体的相同部分
	common() *rtype

    // 返回类型结构体的不同部分
	uncommon() *uncommonType

}

2、反射种类 Kind

种类(Kind) 指的是对象归属的品种,在reflect包中有如下定义:

type 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        // 底层指针
)

3、反射值 Value

reflect.Value 是一个结构体类型,用于获取变量值的信息,可通过 reflect.ValueOf 函数获取修改原始数据类型(某个变量)的值信息。这个结构体没有对外暴露的字段,但是提供了获取或者写入数据的方法:

// Value 类型

type Value struct {

   // 代表的数据类型
   typ *rtype

   // 指向原始数据的指针
   ptr unsafe.Pointer

}

// 一些方法,例如:
// 对可寻址的值返回其地址
func (v Value) Addr() Value

// 将值以 bool 类型返回
func (v Value) Bool() bool

// 将值以字节数组 []bytes 类型返回
func (v Value) Bytes() []byte

// 将值以 interface{} 类型返回
Interface() interface {}

// 使用 int64 设置值
Setlnt(x int64)

...
三、菜鸟实战

实战需求:使用反射获取变量和方法信息

马上安排!

1、创建 g005.go

/*
 * @Author: 菜鸟实战
 * @FilePath: /go110/go-005/g005.go
 * @Description: 反射
 */

 package main

 import (
	 "fmt"
	 "reflect"
	 "runtime"
 )
 
 // typeOf 方法获取变量的信息
 func reflect_type(a interface{}) {
 
	 // 获取 type
	 t := reflect.TypeOf(a)
	 fmt.Printf("a 的类型为: %v \n", t)
 
	 // 获取 kind
	 k := t.Kind()
	 switch k {
	 case reflect.Int64:
		 fmt.Printf("a 为 int64 \n")
	 case reflect.String:
		 fmt.Printf("a 为 string \n")
	 default:
		 fmt.Printf("a 未知 \n")
	 }
 
 }
 
 // valueOf 方法获取变量的值信息
 func reflect_value(a interface{}) {
 
	 // 获取 value
	 v := reflect.ValueOf(a)
	 // 获取 kind
	 k := v.Kind()
 
	 switch k {
	 case reflect.Int64:
		 fmt.Printf("a 为 int64, 存储值为 %d \n", v.Int())
	 case reflect.String:
		 fmt.Printf("a 为 string , 存储值为 %s \n", v.String())
	 default:
		 fmt.Printf("a 未知 \n")
	 }
 
 }
 
 // Field 利用发射获取结构体中的方法和调用
 type Student struct {
	 Name  string
	 Age   int
	 Score float32
 }
 
 func reflect_field() {
	 // 创建结构体变量
	 var s Student = Student{
		 Name:  "Tom",
		 Age:   10,
		 Score: 80,
	 }
 
	 v := reflect.ValueOf(s)
	 t := v.Type()
	 k := t.Kind()
 
	 // 分析 s 的变量类型
	 switch k {
	 case reflect.Struct:
		 fmt.Printf("s 为 struct \n")
		 fmt.Printf("field num of s is : %d \n", v.NumField())
 
		 // field(i) 可以取得字段信息,返回一个 Value 类型的值
		 for i := 0; i < v.NumField(); i++ {
			 field := v.Field(i)
			 // 打印字段名称,类型 和 值
			 fmt.Printf("name : %s, type: %v, value : %v \n", t.Field(i).Name, field.Type().Kind(), field.Interface())
 
		 }
	 default:
		 fmt.Printf("s 不是 struct \n")
	 }
 
 }
 
 // 主函数
 func main() {
	 // 使用内置函数打印
	 println("Hello", "菜鸟实战")
 
	 // type
	 reflect_type(5)
	 reflect_type("China")
 
	 // value
	 reflect_value(10)
	 reflect_value("golang")
 
	 // struct
	 reflect_field()
 
	 // 使用包函数打印
	 fmt.Printf("版本: %s \n", runtime.Version())
 }
 

2、编译和运行

# 1、生成模块依赖
go mod init g005
 
# 2、编译
go build g005.go 
 
# 3、编译后的目录结构
 
└── go-005
    ├── g005
    ├── g005.go
    └── go.mod
 
# 4、运行
go run g005

3、运行结果

Hello 菜鸟实战
a 的类型为: int 
a 未知 
a 的类型为: string 
a 为 string 
a 未知 
a 为 string , 存储值为 golang 
s 为 struct 
field num of s is : 3 
name : Name, type: string, value : Tom 
name : Age, type: int, value : 10 
name : Score, type: float32, value : 80 
版本: go1.17.10 

菜鸟实战,持续学习!